链接.bind()调用JavaScript.出乎意料的结果?

Rub*_*ate 40 javascript

来自MDN:

bind()方法创建一个新函数,在调用时,将其this关键字设置为提供的值

我很高兴看到它在这个例子中起作用:

(function () {
   console.log(this);
}).bind({foo:"bar"})();
Run Code Online (Sandbox Code Playgroud)

哪些日志Object { foo="bar"}.

但是如果我链接另一个绑定调用,甚至是一个"调用"调用,我仍然会调用调用的函数,并将"this"分配给传递给第一个绑定的对象.例子:

(function () {
   console.log(this);
}).bind({foo:"bar"}).bind({oof:"rab"})();
Run Code Online (Sandbox Code Playgroud)

&

(function () {
   console.log(this);
}).bind({foo:"bar"}).call({oof:"rab"});
Run Code Online (Sandbox Code Playgroud)

记录Object { foo="bar"}而不是我期望的记录:Object { oof="rab"}.

无论我链接多少绑定调用,只有第一个看起来有效.

为什么?

这可能有所帮助.我刚刚发现jQuery的版本行为方式相同!:o

jQuery.proxy(
  jQuery.proxy(function() {
      console.log(this);
  },{foo:"bar"})
,{oof:"rab"})();
Run Code Online (Sandbox Code Playgroud)

日志 Object { foo="bar"}

小智 44

很容易想到bind以某种方式修改函数以使用新函数this.在这种(不正确的)解释中,人们认为bind在函数中添加某种魔术标志,告诉它在this下次调用它时使用不同的.如果是这种情况,则应该可以"覆盖"并更改魔术标志.然后人们会问,任意限制这种能力的原因是什么?

但实际上,这不是它的工作原理.bind创建并返回一个函数,该函数在调用时使用特定函数调用第一个函数this.这个新创建的函数的行为,使用指定的this调用原有的功能,是在焚烧时产生的功能时.它不能被更改,因为函数返回的任何其他函数的内部可以在事后更改.

查看一个真正简单的实现可能会有所帮助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);
Run Code Online (Sandbox Code Playgroud)

正如你所看到的那样bound_fn,函数返回的任何地方都bind没有引用this调用绑定函数的函数.它被忽略了,所以

my_bound_fn.call(999, arg)            // 999 is ignored
Run Code Online (Sandbox Code Playgroud)

要么

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

所以我可以绑定从bind"again" 返回的函数,但这不是重新绑定原始函数; 它只是绑定外部函数,它对内部函数没有影响,因为它已经设置为使用this传递给的上下文(值)调用底层函数bind.我可以一次又一次地绑定但是我最终做的就是创建更多外部函数,这些函数可能绑定到某些东西但最终仍然调用从第一个函数返回的最内层函数bind.

因此,说bind"不能被覆盖" 有点误导.

如果我想"重新绑定"一个函数,那么我可以对原始函数进行新的绑定.所以,如果我绑定它一次:

function orig() { }
my_bound_fn = orig.bind(my_obj);
Run Code Online (Sandbox Code Playgroud)

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

my_bound_fn = my_bound_fn.bind(my_other_obj);     // No effect
Run Code Online (Sandbox Code Playgroud)

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

my_other_bound_fn = orig.bind(my_other_obj);
Run Code Online (Sandbox Code Playgroud)


Igo*_*lla 12

我在MDN上发现了这一行:

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

所以也许它一旦被设置就真的无法被覆盖.

  • 这是我不知道的事情,我希望有些聪明的人会对此有所了解 (3认同)

Dav*_*rad 5

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

例如,

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();
Run Code Online (Sandbox Code Playgroud)

或者,node.js的输出如下.

{ foo: 'bar' }
{ fred: 'barney' }
Run Code Online (Sandbox Code Playgroud)

请注意,第一次调用rebind是调用添加到的,Function.prototype因为它是在普通函数上调用的original_fn,但第二次调用是调用将rebind其作为属性添加到绑定函数(并且任何后续调用都将调用此函数,以及).这rebind只是更新receiver并返回相同的函数对象.

receiver通过使其成为命名函数表达式,可以访问绑定函数中的属性.