bind() 和箭头函数 + call() 有何实质区别?

3

考虑到类似ES6的代码:

let a = new SomeClass();
let b = new AnotherClass();

let x = a.someMethod.bind(b, 1, 2, 3);
let y = () => a.someMethod.call(b, 1, 2, 3);
xy之间有任何实质性的区别吗?我知道bind()函数很古老,但现在是否有必要使用它而不是箭头函数呢?
至少对我来说,箭头函数语法比bind()语法更容易阅读,特别是当this仍然从词法环境中具有正常含义时,实际上通常可以避免使用call()。例如,在实践中,bind()会比箭头函数表现得更好(CPU或RAM方面)吗?

1
对我来说,我认为x很清楚。但是,如果我需要动态绑定它们,我会使用y方法,并在其中传递一些参数,例如 y = (a,b) => a.someMethod.call(b, 1, 2, 3); - ikhvjs
“现在还有必要使用它来代替箭头函数吗?” 呃,这似乎是一个不同的问题。你是想问这个吗?因为使用.bind()可以自动处理部分应用,你仍然可以使用另一个函数,但我并不确定你 必须 这样做或者这会更容易。没有必要重新发明轮子。无论如何,重点是,在这种情况下,你所展示的答案与更一般的情况是不同的。 - VLAZ
1
@customcommander 我认为 someMethodAnotherClass 上不存在。 - VLAZ
@ikhvjs 既然你可以使用 y = (a,b) => a.someMethod.call(b, 1, 2, 3); 并且它很容易理解,那么在简单情况下不使用 () => ...call() 是否有任何原因呢? - Mikko Rantalainen
1
@VLAZ 你说得对,部分应用是使用 bind() 的一个例子。即使对于这种情况,我发现 const addTwo = y => add(2, y);const addTwo = add.bind(null, 2); 更容易阅读。 - Mikko Rantalainen
@MikkoRantalainen 如果有5个参数,我想部分填充到五个不同的地方怎么办?也许在一个地方有一两个,也许在另一个地方有一个或零个。是的,我可以继续制作函数,但它开始模糊了我制作它们的原因。这就是部分应用。是的,我知道我概述的情况并不是很典型。然而,我发现仅关注一个例子并声明它适合从中归纳出结论并不公平。 - VLAZ
2个回答

1

尽管箭头函数在性能方面表现良好,但在某些用例中,箭头函数无法代表相同的逻辑。例如,当您使用Promises时,可以像这样编写代码(此示例来源):

function CommentController(articles) {
    this.comments = [];

    articles.getList()
        .then(function (articles) {
            return Promise.all(articles.map(function (article) {
                return article.comments.getList();
            }));
        })
        .then(function (commentLists) {
            return commentLists.reduce(function (a, b) {
                return a.concat(b);
            });
        })
        .then(function (comments) {
            this.comments = comments;
        }.bind(this));
}

请注意,如果最后的bind(this)被删除,会有什么区别。没有简单的方法使用箭头函数符号来改变这个,而不需要大量修改代码。对于像这样的代码,我个人更喜欢使用闭包和变量名而不是this。此外,bind()可用于部分应用程序,这可能更容易阅读对于具有函数式编程背景的人来说
另一方面,如果稍后修改a.someMethod,使用bind()的版本将无法看到更改,因为它在绑定期间获取了对函数的引用。当调用y()时,带有lambda函数的变体将看到a.someMethod的当前值。
关于需要 .bind(this)的其他示例:

let f  =
{
    x: "bind data",
  test: function()
  {
    console.log("1. this=", this);
    setTimeout(function() {
      console.log("2. this=", this);
    }, 0);
    setTimeout(function() {
      console.log("3. this=", this);
    }.bind(this), 0);
  }
}

f.test();


我不理解这个例子。在什么情况下 function (comments) { this.comments = comments; }.bind(this) 不等同于 comments => { this.comments = comments; } - VLAZ
请注意,上面的示例故意没有使用箭头函数符号。如果您仅使用箭头函数,则显然this不会改变。 - Mikko Rantalainen
1
没有简单的方法使用箭头函数符号来改变this,而不需要大量修改代码”是你对这个例子所说的。我看不出 comments => { this.comments = comments; } 有什么区别:1. 不同;2. 不简单。 - VLAZ
我添加了一个额外的示例。请注意 2. this=3. this= 的输出差异。现在,想象一种情况,您有多个嵌套的异步事件处理程序。如果没有 bind(),则从最内部处理程序引用到最外层的 this 是很困难的。 - Mikko Rantalainen
1
前提是使用箭头函数。你的观点是,很难用普通函数用.bind()替换函数。然而,setTimeout(() => { console.log("2. this=", this); }, 0);是等效的。是的,我同意完全不同的做法会有完全不同的结果。但这与此无关。一个没有.bind()工作方式不同的常规函数并不能向我解释为什么在这种情况下不能轻松使用箭头函数。再次强调,这就是我展示的引用。 - VLAZ
显示剩余3条评论

1

let a = function(){};
let b = function(){};

a.m = 1
b.m = 2
a.someMethod = function(x, y, z){ return this.m + x + y + z }

let x = a.someMethod.bind(b, 1, 2, 3);
let y = () => a.someMethod.call(b, 1, 2, 3)

console.log( x(1,2,3) )
console.log( y(1,2,3) )

function goBind() {
    for (var i = 0; i < 1000000; i++) {
        x(1,2,3)
    }
}

function goArrow() {
    for (var i = 0; i < 1000000; i++) {
        y(1,2,3)
    }

}

function race() {
  var start = performance.now();
  goBind();
  console.log('bind: ' + (performance.now() - start));
  start = performance.now();
  goArrow()
  console.log('arrow: ' + (performance.now() - start));
  start = performance.now();
  goBind();
  console.log('bind: ' + (performance.now() - start));
  start = performance.now();
  goArrow()
  console.log('arrow: ' + (performance.now() - start));
  console.log('------');
}
<button onclick="race()">RACE!</button>

基于这个问题:箭头函数在v8中是否比普通函数声明更快(更高效,更轻量级)?

是的,至少在微基准测试中似乎存在相当大的性能差异。请注意,这里不需要b.someMethod的定义。 - Mikko Rantalainen
1
我认为您无法在一般情况下进行优化,因为someMethod()可能会修改this,这意味着b的属性也会被修改。如果使用y = () => a.someMethod方法来调用正确的方法,但是它将拥有错误的this - Mikko Rantalainen
1
你是对的,但从性能方面来看,箭头函数包装函数,而 bind 直接调用它。 - adiian
因此,总结一下,似乎 bind 比嵌入调用的箭头函数要快得多。只是为了好玩,我试图添加一个包装箭头 `let x = () => a.someMethod.bind(b, 1, 2, 3); 在这种情况下,我们将箭头+bind与箭头+call进行比较,前者似乎要快得多,因此性能差异似乎来自于 bind 和 call。 - adiian
1
转念一想,JS引擎可能无法优化掉lambda堆栈帧,因为这些函数的含义存在差异。如果稍后修改a.someMethod,则使用bind()的变体将继续使用旧定义,而lambda方法将重定向到新定义。 - Mikko Rantalainen
显示剩余2条评论

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