Mozilla的bind函数问题

21

我在Mozilla网站上找到一个bind函数的实现,但有个问题。大部分我都理解了,但我不知道这个检查是干什么用的...

this instanceof nop ? this : ( obj || {} ) 

在bind函数中,显然它检查'this'是否为空函数,但为什么需要绑定空函数呢?我已经在Firebug中尝试过了,它可以工作,但是有什么意义呢?只是想增加我的JavaScript知识,所以任何帮助将不胜感激。

if ( !Function.prototype.bind ) {

  Function.prototype.bind = function( obj ) {

    var slice = [].slice,
    args = slice.call(arguments, 1), 
    self = this, 
    nop = function () {}, 
    bound = function () {
      return self.apply( this instanceof nop ? this : ( obj || {} ), 
                          args.concat( slice.call(arguments) ) );    
    };

    nop.prototype = self.prototype;

    bound.prototype = new nop();

    return bound;
  };
}

弹出js控制台并进行评估,看看你得到了什么! - Adam Bergmark
这真的很令人困惑...由于它在调用自身,它可能会使用它来防止无限循环?或者只是为了提高性能? - Halcyon
是的 - 我确实尝试过了 - 它可以工作,但我只是绑定到空函数,所以没有什么真正的发生。只是想知道是否有人能够解释为什么我要这样做,或者为什么它被写成这样。 - mike
另外,为什么要将参数拆分,然后再合并起来?这样快吗?我猜想 arguments.concat([]) 是最快的克隆方式。 - Halcyon
参数第一次被分割,因为如果你像这样做 --- function.bind obj arg1 arg2 - 你会得到一个绑定到一个对象的函数,该对象具有填充的arg1和arg2 - 函数的部分应用程序 --- 然后当您使用args调用实际绑定的函数时,这些args将与您最初绑定函数的args连接在一起 --- 至少这是我能做出的最好解释。 - mike
3个回答

31

它允许您在没有绑定到原始对象的情况下,将绑定函数作为构造函数调用。换句话说,如果您使用new调用“绑定”函数,则它仍将像原始未绑定版本一样正常工作。

以下是一个示例:

var obj = {};

function foo(x) {
    this.answer = x;
}
var bar = foo.bind(obj);   // "always" use obj for "this"

bar(42);
console.log(obj.answer);   // 42

var other = new bar(1);    // Call bar as a constructor
console.log(obj.answer);   // Still 42
console.log(other.answer); // 1

工作原理

为了简化说明,这里是一个简化版的代码,只绑定this,不处理参数或缺少obj参数的情况:

Function.prototype.bind = function( obj ) {
  var self = this,
  nop = function () {},
  bound = function () {
    return self.apply( this instanceof nop ? this : obj, arguments );
  };

  nop.prototype = self.prototype;
  bound.prototype = new nop();

  return bound;
};
Function.prototype.bind 返回的函数在作为函数或构造函数使用时具有不同的行为(请参见 ECMAScript 5 语言规范的第15.3.4.5.1节第15.3.4.5.2节)。主要区别在于当作为构造函数调用时忽略“绑定的this”参数(因为在构造函数内部,this 必须是新创建的对象)。因此,bound 函数需要一种方法来确定它被如何调用。例如,bound(123) vs. new bound(123) 并相应地设置 this

这就是 nop 函数的作用。它实质上充当一个中间的“类”,使得 bound 继承 nopnop 再继承 self(即调用了 bind() 的函数)。该过程在这里设置:

nop.prototype = self.prototype;
bound.prototype = new nop();

当你调用绑定的函数时,它会返回这个表达式:

self.apply( this instanceof nop ? this : obj, arguments ) )

this instanceof nop的工作原理是通过遵循原型链来确定this的任何原型是否等于nop.prototype。通过设置nop.prototype = self.prototypebound.prototype = new nop(),使用new bound()创建的任何对象都将通过bound.prototypeself继承原始原型。因此,在函数调用内部,this instanceof nop(即Object.getPrototypeOf(nop) == nop.prototype)为true,并且self使用this(新创建的对象)进行调用。

在普通函数调用中,'bound()'(没有new)时,this instanceof nop将为false,因此obj将作为this上下文传递,这是绑定函数所期望的。

使用中间函数的原因是避免调用原始函数(在bound.prototype = new nop();行中),原始函数可能具有副作用。


非常有趣,看起来正确 - 我从未想过那个 - 谢谢 - mike
不知道为什么,我不理解上面的nop是如何影响这个答案的。 - Sunny R Gupta
@SunnyRGupta 我猜我回答了它的功能,但没有解释它是如何工作的。它涉及到JavaScript的深层原理,但我会尽力解释清楚。 - Matthew Crumley
1
@SunnyRGupta 我不记得当答案被编辑时,SO是否会通知你,但我为nop()函数添加了一些解释。希望至少能让你有点理解。 - Matthew Crumley
1
@TinaChen bind函数无法调用new self(),因为它会调用该函数,这可能会产生副作用。例如,如果你有这个函数:function f() { alert('oops'); },那么f.bind({})会显示警报,这是令人惊讶的(也是错误的)。nop函数定义在bind内部,什么也不做,因此可以安全调用。 - Matthew Crumley
显示剩余2条评论

1
也许我完全错了,但是:
为什么不仅使用第一个调用bind的函数的原型作为条件,而不是像nop()那样创建一个新函数?对我来说这很有效。
也许创建nop()函数有重要的原因,我还不理解(我是JavaScript新手)。
Function.prototype.bind = function( obj ) {

var slice = [].slice,
args = slice.call(arguments, 1), 
self = this,  
bound = function () {
  return self.apply( this instanceof self ? this : ( obj || {} ), 
                      args.concat( slice.call(arguments) ) );    
};

bound.prototype = self.prototype;

return bound;
};

bound.prototype = self.prototype并不意味着bound instanceof self == true。因此检查this instanceof self?是行不通的。另一方面,bound.prototype = new self()确实意味着bound instanceof self == true(可以在浏览器控制台中轻松检查)。但是调用self()可能会带来副作用,正如@MatthewCrumley所提到的那样。因此,您需要一个全新的函数来使用new进行调用。 - DmitriyBelovol

0

我认为这是一个简短的符号表示 (typeof obj != "undefined") ? obj : {}

也就是说,如果obj未定义,则返回一个空对象({}是一个空对象)。


嗯——我不确定是不是因为我完全不懂JavaScript,所以我对你的答案不是很明白。让我有些犹豫的部分是:“this instance of nop ? this”,它似乎是在说如果“this”是空函数,则返回“this”,这意味着我们正在绑定空函数。 - mike
2
(obj || {}) 这部分很清楚。让人困惑的是 'nop' 部分。 - Halcyon
抱歉,你是对的,我读得太快了,错过了重点,让我再读一遍 :) - morgar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接