这是一个纯粹的理论问题。
我从“你不懂 js”中学习 javascript,我一直卡在 JS 中 bind 函数的实现上。考虑以下代码:
function foo(something) {
this.a = something;
}
var obj1 = {};
var bar = foo.bind(obj1);
bar(2);
console.log(obj1.a); // 2
var baz = new bar(3);
console.log(obj1.a); // 2
console.log(baz.a); // 3
在上面的代码片段中,我们将foo()绑定(bind)到obj1,所以foo()中的this > 属于 obj1,这就是当我们调用 bar(2) 时 obj1.a 变成 2 的原因。但是 new 运算符可以优先,并且 obj1.a 不会改变,即使 bar(3) 是用 new 调用的。
下面是 MDN 页面为 bind(..) 提供的 polyfill:
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError( "Function.prototype.bind - what " +
"is trying to be bound is not callable"
);
}
var aArgs = Array.prototype.slice.call( arguments, 1 ),
fToBind = this,
fNOP = function(){},
fBound = function(){
return fToBind.apply(
(
this instanceof fNOP &&
oThis ? this : oThis
),
aArgs.concat( Array.prototype.slice.call( arguments ) )
);
}
;
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
根据本书允许新覆盖的部分是:
this instanceof fNOP &&
oThis ? this : oThis
// ... and:
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
所以,现在是重点。根据这本书: “我们实际上不会深入解释这种诡计是如何工作的(它很复杂并且超出了我们的范围),但本质上实用程序确定是否使用 new 调用了硬绑定(bind)函数(导致新构造的对象成为它的this),如果是这样,它会使用新创建的 this 而不是之前为此指定的硬绑定(bind)。”
bind() 函数中的逻辑如何允许 new 运算符覆盖硬绑定(bind)?
最佳答案
首先,了解对象原型(prototype)(表示规范为 [[Prototype]] 和可通过函数 Object.getPrototypeOf 或弃用的 __proto__ 属性)和名称为 prototype 的函数的属性。每个函数都有一个名为 prototype 的属性,当使用 new 调用函数时会使用该属性。
当您使用 new 调用一个函数时,该函数被提供一个 this 值设置为一个新构造的对象,其原型(prototype)(即 [[ Prototype]]) 设置为被调用函数的 prototype 属性。也就是说,当您调用 new Foo() 时,当 Foo 中的代码运行时, this 值将是形式
{ [[Prototype]]: Foo.prototype }
让我们简要地认识一下变量的转换:
fToBind 是被绑定(bind)的函数:对于 foo.bind(...),foo 是 fToBind.fBound 是 fToBind 的绑定(bind)版本;它是 bind 操作的返回值。 fBound 充当原始 fToBind 函数的看门人,并决定 this 值 fToBind 在被调用时获得什么.oThis 是提供给 bind 的第一个参数,即绑定(bind)到函数的 this 的对象。fNOP 是一个函数,其 prototype 属性设置为 fToBind.prototypefBound.prototype = new fNOP() 使这些为真:
Object.getPrototypeOf(fBound.prototype) === fNOP.prototype
Object.getPrototypeOf(fBound.prototype) === fToBind.prototype
当使用 new 调用 fBound 时,提供给 fBound 的 this 的形式为
{ [[Prototype]]: fBound.prototype }
和fBound.prototype是一个对象的形式
{ [[Prototype]]: fNOP.prototype }
使 this 的完整形式等同于
{ [[Prototype]]: { [[Prototype]]: fNOP.prototype } }
因此,fNOP.prototype 在 fBound 被 new 调用时,在新创建的 。这正是 this 对象的原型(prototype)链中object instanceof constructor 操作测试的内容:
The
instanceofoperator tests the presence ofconstructor.prototypeinobject's prototype chain.
这里&&与三元运算的顺序是:
(this instanceof fNOP && oThis) ? this : oThis
如果 this 在它的原型(prototype)链中有 fNOP.prototype 并且原始的 bind 调用被赋予一个真值第一个参数绑定(bind)到函数,然后使用 new 调用时提供给 fBound 的自然创建的 this 并将其提供给 fToBind 而不是绑定(bind)的 this。
关于javascript - `new` 运算符如何能够覆盖硬绑定(bind),在 Function.prototype.bind(..),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46528091/
它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput
我在理解Enumerator.new方法的工作原理时遇到了一些困难。假设文档中的示例:fib=Enumerator.newdo|y|a=b=1loopdoy[1,1,2,3,5,8,13,21,34,55]循环中断条件在哪里,它如何知道循环应该迭代多少次(因为它没有任何明确的中断条件并且看起来像无限循环)? 最佳答案 Enumerator使用Fibers在内部。您的示例等效于:require'fiber'fiber=Fiber.newdoa=b=1loopdoFiber.yieldaa,b=b,a+bendend10.times.m
请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是
我正在尝试在Rails上安装ruby,到目前为止一切都已安装,但是当我尝试使用rakedb:create创建数据库时,我收到一个奇怪的错误:dyld:lazysymbolbindingfailed:Symbolnotfound:_mysql_get_client_infoReferencedfrom:/Library/Ruby/Gems/1.8/gems/mysql2-0.3.11/lib/mysql2/mysql2.bundleExpectedin:flatnamespacedyld:Symbolnotfound:_mysql_get_client_infoReferencedf
我明白了:x,(y,z)=1,*[2,3]x#=>1y#=>2z#=>nil我想知道为什么z的值为nil。 最佳答案 x,(y,z)=1,*[2,3]右侧的splat*是内联扩展的,所以它等同于:x,(y,z)=1,2,3左边带括号的列表被视为嵌套赋值,所以它等价于:x=1y,z=23被丢弃,而z被分配给nil。 关于ruby-带括号和splat运算符的并行赋值,我们在StackOverflow上找到一个类似的问题: https://stackoverflow
我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的
如thisanswer中所述,Array.new(size,object)创建一个数组,其中size引用相同的object。hash=Hash.newa=Array.new(2,hash)a[0]['cat']='feline'a#=>[{"cat"=>"feline"},{"cat"=>"feline"}]a[1]['cat']='Felix'a#=>[{"cat"=>"Felix"},{"cat"=>"Felix"}]为什么Ruby会这样做,而不是对object进行dup或clone? 最佳答案 因为那是thedocumenta
问题是:除了在“OperatorExpressions”?例如:1%!2 最佳答案 是的,可以创建自定义运算符,但有一些注意事项。Ruby本身并不直接支持它,但是superatorsgem做了一个巧妙的把戏,将运算符链接在一起。这允许您创建自己的运算符,但有一些限制:$geminstallsuperators19然后:require'superators19'classArraysuperator"%~"do|operand|"#{self}percent-tilde#{operand}"endendputs[1]%~[2]#Out
一边学习thisRailscast我从Rack中看到了以下源代码:defself.middleware@middleware||=beginm=Hash.new{|h,k|h[k]=[]}m["deployment"].concat[[Rack::ContentLength],[Rack::Chunked],logging_middleware]m["development"].concatm["deployment"]+[[Rack::ShowExceptions],[Rack::Lint]]mendend我的问题是关于第三行。什么是传递block{|h,k|h[k]=[]}到Has
是否有可能以某种方式访问Class.new范围内的a?a=5Class.new{defb;aend}.new.b#NameError:undefinedlocalvariableormethod`a'for#:0x007fa8b15e9af0>#:in`b' 最佳答案 即使@MarekLipka的回答是正确的——改变变量范围总是有风险的。这是可行的,因为每个block都带有创建它的上下文,因此您的局部变量a突然变得不那么局部了——它变成了一个“隐藏的”全局变量:a=5object=Class.new{define_method(