在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等,并通过执行以下步骤返回一个新的函数对象:
- 让Target成为这个值。
- 如果IsCallable(Target)为false,则引发TypeErrorexception。
- 假设A是一个新的(可能是空的)内部列表,这个依次在thisArg(arg1,arg2等)之后提供的所有参数值。
- 设F是新的本机ECMAScript对象 。
- 按照8.12的规定设置F的[[Get]]除外的所有内部方法。
- 按照15.3.5.4的规定设置F的[[Get]]内部属性。
- 将F的[[TargetFunction]]内部属性设置为Target。
- 将F的[[BoundThis]]内部属性设置为thisArg的值。
- 将F的[[BoundArgs]]内部属性设置为A.
- 将F的[[Class]]内部属性设置为“Function”。
- 将F的[[Prototype]]内部属性设置为15.3.3.1中指定的标准内置函数原型对象。
- 按照15.3.4.5.1的描述设置F的[[Call]]内部属性。
- 按照15.3.4.5.2的描述设置F的[[Construct]]内部属性。
- 按照15.3.4.5.3的描述设置F的[[HasInstance]]内部属性。
- 如果Target的[[Class]]内部属性是“Function”,那么a。 令L为目标的长度属性减去A的长度。 将F的长度自己的属性设置为0或L,以较大者为准。
- 否则将F的长度自己的属性设置为0。
- 将F的长度属性的属性设置为15.3.5.1中指定的值。
- 将F的[[Extensible]]内部属性设置为true。
- 让thrower成为[[ThrowTypeError]]函数Object(13.2.3)。
- 调用参数“caller”,PropertyDescriptor {[[Get]]:thrower,[[Set]]:thrower,[[Enumerable]]:false,[[Configurable]]:False的F的[[DefineOwnProperty]]内部方法}和false。
- 调用参数“arguments”,PropertyDescriptor {[[Get]]:thrower,[[Set]]:thrower,[[Enumerable]]:false,[[Configurable]]:False的F的[[DefineOwnProperty]]内部方法}和false。
- 返回F
并且当你在用你的bind
创build的对象上调用一个function
时:
15.3.4.5.1 [[Call]]
当使用绑定函数创build的函数对象F的[[Call]]内部方法用此值和参数ExtraArgs的列表调用时,将执行以下步骤:
- 让boundArgs是F的[[BoundArgs]]内部属性的值。
- 让bound这是F的[[BoundThis]]内部属性的值。
- 设目标为F的[[TargetFunction]]内部属性的值。
- 让args成为一个新的列表,其中包含与列表boundArg相同的值,顺序与列表ExtraArgs相同,顺序相同。
- 返callback用提供boundThis的[[Call]]内部方法的结果作为该值并提供参数作为参数
调用指定每个函数的调用方式。 有点类似于JavaScript的call
:
someFunction.[[call]](thisValue, arguments) { }
但是,当在绑定函数上使用[[call]]
, thisValue
会被[[BoundThis]]
的值覆盖。 在第二次调用bind
的情况下,您试图覆盖第一个的[[BoundThis]]
被replace为[[BoundThis]]
,对thisValue
的值基本上不产生任何影响:
boundFunction.[[call]](thisValue, arguments) { thisValue = boundFunction.[[BoundThis]]; }
您会注意到,如果您尝试使用call
或apply
那么它们也将不起作用,因为当[[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()