2011-09-02 32 views
22

我有一个关于EcmaScript-5 Function.prototype.bind实现的非常有趣的问题。通常当你使用绑定,你做这种方式:Function.prototype.bind

var myFunction = function() { 
    alert(this); 
}.bind(123); 

// will alert 123 
myFunction(); 

好了,这很酷,但什么是假设我们这样做的时候会发生什么?

// rebind binded function 
myFunction = myFunction.bind('foobar'); 
// will alert... 123! 
myFunction(); 

据我所知,这是在Function.prototype.bind是如何实现的(https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind)方面完全合乎逻辑的行为。但在现实生活中,这完全是无用的行为,不是吗?问题是:它是缺陷还是功能?如果这是一个错误,为什么它没有提到?如果这是一项功能,那么为什么Google Chrome与原生“绑定”实现的行为方式完全相同?

为了使它更清楚,在我看来,你会更有意义,这里是实现Function.prototype.bind的代码片段一点点不同:

if (!Function.prototype.bind) { 
    Function.prototype.bind = function() { 
     var funcObj = this; 
     var original = funcObj; 
     var extraArgs = Array.prototype.slice.call(arguments); 
     var thisObj = extraArgs.shift(); 
     var func = function() { 
      var thatObj = thisObj; 
      return original.apply(thatObj, extraArgs.concat(
       Array.prototype.slice.call(
        arguments, extraArgs.length 
       ) 
      )); 
     }; 
     func.bind = function() { 
      var args = Array.prototype.slice.call(arguments); 
      return Function.prototype.bind.apply(funcObj, args); 
     } 
     return func; 
    }; 
} 

所以现在试试这个:

// rebind binded function 
myFunction = myFunction.bind('foobar'); 
// will alert... "foobar" 
myFunction(); 

在我看来,取代“本”更有意义......

所以你们认为是什么呢?

+2

这是一个功能。如果你可以重写它,它不会真的“受限制”,会吗?详细信息可以在规范中找到:http://ecma262-5.com/ELS5_HTML.htm#Section_15.3.4.5 –

+1

肯定,但如果是,那么什么是确定的功能已经被绑定到方式什么?或者为什么它在试图重新绑定时不会抛出任何异常?问题在于,如果你不知道函数是否被绑定,或者你处理了它的第一个副本,那么调试非常困难。昨天发生了这样的事情,我花了将近一天的时间来找出问题的根源...... – Ruslan

+1

好吧,无论如何,似乎没有人会回答这个问题,所以如果别人会有同样的问题,我创建了一个workaroud,你可以在这里找到:http://www.angrycoding.com/2011/09/to-bind-or-not-to-bind-that-is-in.html – Ruslan

回答

14

Function.prototype.bind的先例是在各种JS框架中实现这个想法。据我所知,他们都没有允许this-绑定被随后的绑定改变。你可能会问,为什么没有一个人允许这种绑定变化,至于为什么ES5不允许。

你不是我听过的唯一一个觉得奇怪的人。克里斯莱里,谁在Mozilla的JS引擎工作(像我一样),认为这有点奇怪,几个月前在Twitter上提出这个问题。我记得其中一个Mozilla实验室黑客质疑是否有某种方法可以“解除”某个函数,从中提取目标函数。 (如果你可以这样做,你当然可以将它绑定到不同的this,至少如果你也可以提取绑定的参数列表来传递它。)

我不记得当讨论的问题时正在指定bind。但是,当这些东西被抛出时,我并没有特别关注es-discuss邮件列表。也就是说,我不相信ES5正在寻求在该地区进行创新,只是“铺设一条牛通路”,借用一句话。

如果你写了一个足够详细的提案,你可能可以提出一些内省的方法来解决这些问题以进行讨论。另一方面,绑定是信息隐藏机制的一种形式,它会削弱它的采用。如果你有时间,可能值得尝试提出一些建议。我的猜测是信息隐藏的担忧会阻止提案被采纳。但这只是一个猜测,可能是错误的。只有一种方法可以找出...

+0

非常感谢您的回答,它非常有用。你能否描述提出这些建议的程序?我认为这不仅对这种情况有用。先谢谢你。 – Ruslan

+0

我在实际的标准化工作中没有足够的知识去了解这个过程(我不相信这是一个完全正式的过程),但是发送邮件到[es-discuss](https://mail.mozilla.org/listinfo/es-discuss)将会进行滚动。您也可以尝试在irc.mozilla.org上的#jslang频道询问;这是我所知道的唯一真正与JS语言标准制定者有关的真实视频聚会,因此有人可能会知道如何获得完整的提案。 –

2

当你绑定一个函数时,你需要一个新的函数来忽略它自己的这个伪参数,并用这个固定值调用原始函数。

另一次绑定此函数具有完全相同的行为。如果绑定会以某种方式将新的补丁添加到它中,那么对于已经绑定的函数来说,它将有特殊情况。

换句话说,绑定对“正常”函数的作用与对bind返回的函数的作用完全相同,并且在没有压倒性因素的情况下,保持函数的语义复杂度较低是很好的工程 - 更容易记住绑定对函数的作用,如果它以完全相同的方式处理所有输入函数,而不是专门处理某些输入函数。

我觉得你的困惑是,您查看绑定为修改现有的功能,你因此,如果您再次修改它,你所期望的原有功能进行再次修改。但是,绑定不会修改任何内容,它会创建具有特定行为的新函数。新功能本身就是一个功能,它不是原始功能的魔术修补版本。

因此,对为什么它被标准化或发明它的方式并不神秘:绑定返回提供了一个新的这个,并预置参数的函数,而工作原理相同的所有功能。最简单的语义。

只有这样,它实际上是安全的使用 - 如果绑定会使已绑定功能和“正常”的人之间的差异,人们就必须结合前进行测试。一个很好的例子是jQuery.each,它传递了一个新的。如果绑定会使用特殊的绑定函数,那么将没有安全的方法将绑定函数传递到jQuery.each中,因为这将在每次调用时被覆盖。为了解决这个问题,jQuery.each将不得不以某种方式使用特殊的绑定函数。

+0

有人可能会认为绑定不应该触及这个,因为这将与其他JS行为一致。但是,我想说的绑定最常见的应用就是一个方法调用转换成一个函数调用(JavaScript有可能作为回调传递没有内置的方法调用的对象 - 绑定填补这个洞) –

+0

至于你的预期行为,你应该问自己为什么你期望绑定在一个新的值中打补丁,但不能补充新的参数。这很不一致。 –

+0

我完全不同意! bind()的工作方式绝对违反直觉。问题是,你必须知道一个函数已经绑定。在调用.bind()时,你永远不能确定你将从函数中得到什么。有时你会得到预期的行为。有时候你没有。它就像这样:一个绑定函数具有特殊的语义,而不仅仅是一个函数。在我看来,一个绑定函数应该只是一个函数,你可以从Function.prototype中应用所有的东西。否则,给它一个不同的原型,例如BoundFunction并从中删除.bind()。 – NicBright

相关问题