ES6箭头函数和使用Function.prototype.bind绑定的函数有哪些不同(如果有的话)?

56

我认为,在ES6中,下面两个函数非常相似:nearly

function () {
  return this;
}.bind(this);

() => {
  return this;
};

最终结果似乎相同:箭头函数会生成一个JavaScript函数对象,其this上下文与创建它们的this值绑定到相同的值。

显然,在一般情况下,Function.prototype.bind比箭头函数更加灵活:它可以绑定到除局部this之外的其他值,并且可以在任何时间绑定任何函数的this,甚至在初始创建之后很长一段时间。但是,我问的不是bind本身与箭头函数的不同之处,而是箭头函数与立即使用bindthis之间的区别。

ES6中这两个结构之间有区别吗?





















使用 bind 本质上是在创建两个函数。除此之外,你提到的事情和箭头函数具有更简洁的语法之外,没有任何区别。 - Felix Kling
由于这个问题并没有寻求,而且它的答案也没有提供任何你可能有充分理由使用 bind 而不是箭头函数的时间,因此我已经提出了一个新问题,讨论何时使用 bind:https://runkit.com/embed/yhv29j5sybvn - hippietrail
1
bind 版本中,它返回一个新函数,该函数将参数部分应用于 bind。箭头没有 this,因此使用它就像来自更高作用域的任何自由变量一样。 - Sylwester
3个回答

39

没有(显著的)区别。

好吧,那有一点为时过早了。箭头函数有三个微小的独特之处。

  1. 箭头函数不能与 new 一起使用。

    这意味着它们没有 prototype 属性,不能用经典语法创建对象。

new (() => {}) // TypeError: () => {} is not a constructor

虽然这样做可能会更好,但是使用new关键字来绑定函数将没有太多意义。

  • 箭头函数不能访问普通JavaScript函数可以访问的特殊arguments对象。

  • (() => arguments)(1, 2, 3) // ReferenceError: arguments is not defined
    

    这可能是一个小陷阱。据推测,这是为了消除JavaScript的其他怪异行为之一。 arguments 对象是一种特殊的东西,它具有奇怪的行为,因此抛弃它并不奇怪。

    相反,ES6有扩展运算符可以完成相同的操作而无需使用任何魔法隐藏变量:

    ((...args) => args)(1, 2, 3) // [1, 2, 3]
    
  • 箭头函数没有自己的 new.target 属性,它们使用其封闭函数的 new.target (如果存在)。

    这符合删除箭头函数中“神奇”引入值的其他更改。考虑到上面提到的箭头函数无法与 new 一起使用,因此这种特定的更改尤为明显。

  • 除此之外,语义上来说,箭头函数就像绑定函数一样。由于它们不必携带额外的负担并且不需要先从普通函数转换,所以箭头函数可能更具性能优势,但它们在行为上完全相同。


    4
    并不是因为“奇怪的行为”(在严格模式下已经修复)才放弃了arguments,我不会这么说。相反,箭头函数可以访问其封闭函数的arguments对象,这与它们访问其this绑定和new.target一致。 - Bergi
    4
    即使在严格模式下,arguments 仍然有相当奇怪的行为:它仍然不是一个数组,因此展开操作符仍然更加实用和可预测。 - Alexis King
    @AlexisKing,你应该在你的回答中明确提到接受父级上下文。 - richardpringle

    31

    有几个不同之处:

    • 箭头函数不能被构造。虽然箭头函数和绑定函数都没有.prototype属性,但前者在用new调用时会抛出异常,而后者只会忽略绑定的值并将目标函数作为构造函数调用(虽然是带有部分应用的绑定参数)。

    function F() {}
    var f = () => {},
        boundF = F.bind({});
    console.log(new boundF(), new boundF instanceof F) // {}, true
    console.log(new f) // TypeError
    
  • 箭头函数确实具有词法argumentsnew.targetsuper(不仅是词法this)。对箭头函数的调用不会初始化它们中的任何一个,它们只是继承自定义有箭头函数的那个函数。在绑定函数中,它们只是引用目标函数的相应值。

  • 箭头函数实际上并不绑定this值。相反,它们没有this值,当您使用this时,它会像变量名称一样在词法作用域中查找。这使您能够在this尚不可用时懒惰地定义箭头函数:

  • class X extends Object {
        constructor() {
             var f = () => this, // works
                 boundF = function(){ return this; }.bind(this);
    //                                                    ^^^^ ReferenceError
             super(); // initialises `this`
             console.log(f(), f() == this); // {}, true
        }
    }
    new X;
    
  • 箭头函数不能是生成器函数(尽管它们可以返回生成器)。你可以在生成器函数上使用.bind(),但是没有办法使用箭头函数来表达这个意思。


  • 9

    这里有一个更加微妙的区别:

    箭头函数可以省略跟在=>后面的{}大括号,直接返回一个值,而无需使用'return'关键字。

    var f=x=>x;           console.log(f(3));  // 3
    var g=x=>{x};         console.log(g(3));  // undefined
    var h=function(x){x}; console.log(h(3));  // undefined
    var i=x=>{a:1};       console.log(i(3));  // undefined
    var j=x=>({a:1});     console.log(j(3));  // {a:1}
    

    2
    我认为这不是一个特别有用的答案。是的 - 大括号可以省略 - 这很方便,但这只是语法上的差异,而不是功能上的差异。 - dwjohnston
    我认为这是一个非常优秀的答案,特别是如果你不熟悉所有简写语法。 - Rid Iculous

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