22

I've got pretty interesting question about EcmaScript-5 Function.prototype.bind implementation. Usually when you use bind, you do it this way:

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

// will alert 123
myFunction();

Okay so that's cool, but what is suppose to happen when we do this?

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

I understand that it's completely logical behavior in terms of how Function.prototype.bind is implemented (https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind). But in real life conditions it's completely useless behavior isn't it? The question is: is it bug or feature? If it's a bug, why it's nowhere mentioned? If it's a feature, why then Google Chrome with native "bind" implementation behaves absolutely the same way?

To make it more clear, what in my opinion would make more sense, here is the code snippet that implements Function.prototype.bind a little bit differently:

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;
    };
}

So now try this:

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

In my opinion, replacing "this" makes more sense...

So what do you guys think about it?

4

2 回答 2

14

先例Function.prototype.bind是在各种 JS 框架中实现该想法。据我所知,它们都不允许this通过后续绑定更改 -binding。你可能会问为什么没有一个允许 this-binding 更改,就像问为什么 ES5 不允许它一样。

你不是我听说的唯一一个认为这很奇怪的人。负责 Mozilla JS 引擎的 Chris Leary(和我一样)认为这有点奇怪,几个月前在 Twitter 上提出了这个问题。并且以某种不同的形式,我记得 Mozilla 实验室的一个黑客质疑是否有某种方法可以“取消绑定”一个函数,以从中提取目标函数。(如果你能做到这一点,你当然可以将它绑定到不同this的 ,至少如果你还可以提取绑定的参数列表来传递它。)

我不记得在bind指定时讨论的问题。然而,当这些东西被散列出来时,我并没有特别关注 es-discuss 邮件列表。也就是说,我认为 ES5 并没有在该领域进行太多创新,借用一句话来说,只是“铺平道路”。

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

于 2011-09-06T01:30:13.320 回答
2

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

再次绑定此函数具有完全相同的行为。如果 bind 会以某种方式将新的 this 修补到其中,则它必须针对已绑定的函数进行特殊处理。

换句话说,bind 在“普通”函数上的工作方式与它在 bind 返回的函数上的工作方式完全相同,并且在没有覆盖因素的情况下,将函数的语义复杂度保持在较低水平是一个很好的工程——更容易记住什么是 bind如果它以完全相同的方式处理所有输入函数,而不是专门处理某些输入函数,则对函数执行此操作。

我认为您的困惑是您将绑定视为修改现有函数,因此,如果您再次修改它,您希望原始函数再次被修改。但是, bind 不会修改任何东西,它会创建一个具有特定行为的新函数。新功能本身就是一个功能,它不是原始功能的神奇补丁版本。

因此,它为什么被标准化或发明它的方式并不神秘:bind 返回一个函数,该函数提供一个新的 this 并预先添加参数,并且在所有函数上的工作方式都相同。最简单的语义。

只有这样才能真正安全地使用 - 如果 bind 会在已经绑定的函数和“正常”函数之间产生差异,则必须在绑定之前进行测试。一个很好的例子是 jQuery.each,它传递了一个新的 this。如果 bind 对绑定函数进行特殊处理,则没有安全的方法将绑定函数传递给 jQuery.each,因为这将在每次调用时被覆盖。为了解决这个问题,jQuery.each 必须以某种方式特殊绑定函数。

于 2013-07-25T16:24:03.440 回答