在JavaScript中调用.bind()调用。 意外的结果?

从MDN :

bind()方法创build一个新的函数,当被调用时,它的this关键字被设置为提供的值

我可以高兴地看到它在这个例子中工作:

(function () { console.log(this); }).bind({foo:"bar"})(); 

其中loggingObject { foo="bar"}

但是,如果我链接另一个绑定调用,甚至是“调用”调用,我仍然得到调用“this”分配给传递给第一个绑定的对象的函数。 例子:

 (function () { console.log(this); }).bind({foo:"bar"}).bind({oof:"rab"})(); 

 (function () { console.log(this); }).bind({foo:"bar"}).call({oof:"rab"}); 

log Object { foo="bar"}而不是我所期望的: Object { oof="rab"}

不pipe有多less绑定电话我链,只有第一个似乎有影响。

为什么?

这可能有帮助。 我刚刚发现jQuery的版本行为是一样的! :o

 jQuery.proxy( jQuery.proxy(function() { console.log(this); },{foo:"bar"}) ,{oof:"rab"})(); 

日志Object { foo="bar"}

很容易想到bind就像修改一个函数来使用新的一样。 在这个(不正确的)解释中,人们认为bind是为函数添加某种魔术标志,告诉它在下一次被调用时使用不同的。 如果是这样的话,那么应该可以“覆盖”并改变魔法标志。 然后有人会问,这是什么原因来任意限制这样做的能力呢?

但事实上,这不是它的工作原理。 bind创build并返回一个新的函数 ,当被调用时调用第一个函数。 这个新创build的函数的行为,使用指定的this来调用原始函数, 创build函数时被烧入 。 事实上,函数返回的任何其他函数的内部都不能改变。

这可能有助于看一个真正简单的bind实现:

 // NOT the real bind; just an example Function.prototype.bind = function(ctxt) { var fn = this; return function bound_fn() { return fn.apply(ctxt, arguments); }; } my_bound_fn = original_fn.bind(obj); 

正如你所看到的,在bound_fn无处可bound_fn ,从bind返回的函数是否指向调用绑定函数的函数。 它被忽略,所以

 my_bound_fn.call(999, arg) // 999 is ignored 

要么

 obj = { fn: function () { console.log(this); } }; obj.fn = obj.fn.bind(other_obj); obj.fn(); // outputs other_obj; obj is ignored 

所以我可以绑定从“ bind ”返回的函数,但是这不是重新绑定原来的function; 它只是绑定了外部函数,这对内部函数没有任何影响,因为它已经被设置为通过bind的上下文( this值)来调用底层函数。 我可以一次又一次地绑定,但是我最终所做的是创build更多的外部函数,这些函数可能会绑定到某些东西,但最终还是会调用从第一个bind返回的最内层函数。

因此,说“ bind不能被覆盖”是有些误导的。

如果我想“重新绑定”一个函数,那么我可以对原函数做一个新的绑定。 所以如果我把它绑定一次:

 function orig() { } my_bound_fn = orig.bind(my_obj); 

然后我想安排我的原始函数与其他一些调用,然后我不重新绑定函数:

 my_bound_fn = my_bound_fn.bind(my_other_obj); // No effect 

相反,我只是创build一个新的函数绑定到原来的:

 my_other_bound_fn = orig.bind(my_other_obj); 

我在MDN上find了这一行:

bind()函数创build一个新的函数(一个绑定函数),该函数具有与被调用的函数(绑定函数的目标函数)相同的函数体(ECMAScript 5术语中的内部调用属性),该值绑定到bind()的第一个参数, 不能被覆盖。

所以也许它一旦被设置真的不能被覆盖。

torazaburo的优秀答案给了我一个主意。 对于一个类似于绑定的函数来说,可能不是将接收者(this)烘焙到闭包中的调用中,而是将其作为函数对象的属性,然后在调用时使用它。 这将允许rebind在进行调用之前更新属性,从而有效地提供您期望的重新绑定结果。

例如,

 function original_fn() { document.writeln(JSON.stringify(this)); } Function.prototype.rebind = function(obj) { var fn = this; var bound = function func() { fn.call(func.receiver, arguments); }; bound.receiver = obj; bound.rebind = function(obj) { this.receiver = obj; return this; }; return bound; } var bound_fn = original_fn.rebind({foo: 'bar'}); bound_fn(); var rebound_fn = bound_fn.rebind({fred: 'barney'}); rebound_fn(); 

好吧,这将主要是猜测,但我会尝试通过它的理由。

ECMAScript规范(目前是closures的)声明了bind函数(强调我自己的):

15.3.4.5 Function.prototype.bind(thisArg [,arg1 [,arg2,…]])

bind方法使用一个或多个参数thisArg和(可选)arg1,arg2等,并通过执行以下步骤返回一个新的函数对象:

  1. 让Target成为这个值。
  2. 如果IsCallable(Target)为false,则引发TypeErrorexception。
  3. 假设A是一个新的(可能是空的)内部列表,这个依次在thisArg(arg1,arg2等)之后提供的所有参数值。
  4. 设F是新的本机ECMAScript对象
  5. 按照8.12的规定设置F的[[Get]]除外的所有内部方法。
  6. 按照15.3.5.4的规定设置F的[[Get]]内部属性。
  7. 将F的[[TargetFunction]]内部属性设置为Target。
  8. 将F的[[BoundThis]]内部属性设置为thisArg的值。
  9. 将F的[[BoundArgs]]内部属性设置为A.
  10. 将F的[[Class]]内部属性设置为“Function”。
  11. 将F的[[Prototype]]内部属性设置为15.3.3.1中指定的标准内置函数原型对象。
  12. 按照15.3.4.5.1的描述设置F的[[Call]]内部属性。
  13. 按照15.3.4.5.2的描述设置F的[[Construct]]内部属性。
  14. 按照15.3.4.5.3的描述设置F的[[HasInstance]]内部属性。
  15. 如果Target的[[Class]]内部属性是“Function”,那么a。 令L为目标的长度属性减去A的长度。 将F的长度自己的属性设置为0或L,以较大者为准。
  16. 否则将F的长度自己的属性设置为0。
  17. 将F的长度属性的属性设置为15.3.5.1中指定的值。
  18. 将F的[[Extensible]]内部属性设置为true。
  19. 让thrower成为[[ThrowTypeError]]函数Object(13.2.3)。
  20. 调用参数“caller”,PropertyDescriptor {[[Get]]:thrower,[[Set]]:thrower,[[Enumerable]]:false,[[Configurable]]:False的F的[[DefineOwnProperty]]内部方法}和false。
  21. 调用参数“arguments”,PropertyDescriptor {[[Get]]:thrower,[[Set]]:thrower,[[Enumerable]]:false,[[Configurable]]:False的F的[[DefineOwnProperty]]内部方法}和false。
  22. 返回F

并且当你在用你的bind创build的对象上调用一个function时:

15.3.4.5.1 [[Call]]

当使用绑定函数创build的函数对象F的[[Call]]内部方法用此值和参数ExtraArgs的列表调用时,将执行以下步骤:

  1. 让boundArgs是F的[[BoundArgs]]内部属性的值。
  2. 让bound这是F的[[BoundThis]]内部属性的值。
  3. 设目标为F的[[TargetFunction]]内部属性的值。
  4. 让args成为一个新的列表,其中包含与列表boundArg相同的值,顺序与列表ExtraArgs相同,顺序相同。
  5. 返callback用提供boundThis的[[Call]]内部方法的结果作为该值并提供参数作为参数

调用指定每个函数的调用方式。 有点类似于JavaScript的call

 someFunction.[[call]](thisValue, arguments) { } 

但是,当在绑定函数上使用[[call]]thisValue会被[[BoundThis]]的值覆盖。 在第二次调用bind的情况下,您试图覆盖第一个的[[BoundThis]]被replace为[[BoundThis]] ,对thisValue的值基本上不产生任何影响:

 boundFunction.[[call]](thisValue, arguments) { thisValue = boundFunction.[[BoundThis]]; } 

您会注意到,如果您尝试使用callapply那么它们也将不起作用,因为当[[call]]调用下一个函数时,将尝试覆盖thisValue属性。

这些简单的bind()函数如何解释它更好。

这里是一次函数绑定的样子:

 function bound_function() { function original_function() { console.log(self); } var self = 1; original_function(); } bound_function() 

这是如果我们包装原来的function两次会发生什么:

 function bound_function2() { function bound_function1() { function original_function() { console.log(self); } var self = 1; original_function(); } var self = 2; bound_function1(); } bound_function2()