【问题标题】:Chaining 'bind' and 'call' in JavaScript?在 JavaScript 中链接“绑定”和“调用”?
【发布时间】:2026-01-01 21:15:02
【问题描述】:

当我阅读此answer 时,找到var g = f.call.bind(f);。第一眼看不懂。

那么是不是有一些直接的含义,有一些合适的使用场景呢?

当你在链接中使用call(or apply)bind 或两者时,会发生什么?有什么法律吗?

【问题讨论】:

  • 您是否尝试过使用一些参数调用g,并查看在f 中如何访问它们?
  • 看看this

标签: javascript chaining


【解决方案1】:

var g = f.call.bind(f);。第一眼看不懂。

我假设您熟悉 .call().bind() Function 方法?好吧,它将f.call 方法绑定到函数f

注意f.call 只是Function.prototype.call,我们将它作为f 上的属性访问并不重要,因为我们不在这里调用它。

那么它有什么直接的含义吗?

当我们查看 ES6 等效项时,它可能会变得更加明显:

(Function.prototype.call).bind(f) // or
(f.call).bind(f) // is basically
(...args) => f.call(...args) // or more clear
(ctx, ...args) => f.call(ctx, ...args)

是否有一些合适的使用场景?

好吧,既然您知道它的作用,是的。它可以采用原型方法并将其转换为将实例作为第一个参数的静态函数。一个例子:

function Example(n) { this.name = n; }
Example.prototype.display = function() { console.log(this.name); }

Example.display = Function.call.bind(Example.prototype.display);

var e = new Example;
e.display(); // is the same as
Example.display(e);

是否有任何法律可以进一步链接call/apply/bind

是的:和往常一样,只有链中的最后一个属性被实际调用为方法。在上面的示例中,f.call.bind(…)Function.prototype.call.bind(…)Function.call.apply.bind.call.bind(…) 之间没有区别——它总是在 call 上调用 bind

但是,通过将它们作为参数相互传递,您可以执行 some crazy things 或多或少有用。

【讨论】:

    【解决方案2】:

    好问题。让我们从之前在 * 上出现的一个示例开始:将数组中的所有字符串映射为小写。当然可以写

    strings . map(function(string) { return string.toLowerCase(); })
    

    但这似乎有点冗长。我宁愿写

    strings . map(CALL_LOWERCASE_WITH_ELT_AS_THIS)
    

    所以我可以试试

    strings . map(String.prototype.toLowerCase)
    

    或者,使用一些更喜欢的较短的成语

    strings . map(''.toLowerCase)
    

    因为''.toLowerCase 完全等于String.prototype.toLowerCase

    但这当然行不通,因为map 将每个元素传递给指定的函数作为它的第一个参数,而不是作为它的this。因此,我们需要以某种方式指定一个函数,其第一个参数用于调用其他函数作为它的this。当然,这正是 Function.call 所做的:

    function.call(context)
    

    call ("context") 的第一个参数在调用function 时用作this

    那么,问题解决了吗?我们应该可以说:

    strings . map(''.toLowerCase.call)
    

    人们已经尝试过了,然后想知道为什么它不起作用。原因是即使我们将calltoLowerCase 作为回调传递给mapmap 仍然不知道应该使用this''.toLowerCase 调用回调。我们需要明确告诉map 使用哪个this 来调用函数,在map 的情况下,我们可以使用它的第二个“上下文”参数:

    strings . map(''.toLowerCase.call, ''.toLowerCase)
    

    实际上,由于call在任何函数对象上都是相同的,我们可以将其简化为

    strings . map(Function.call, ''.toLowerCase)
    

    这样可以很好地完成工作。

    然而,虽然map 提供了第二个“上下文”参数来指定this 来调用回调,但这并不是我们可以依赖的在所有情况下都可用的东西。我们需要一种更通用的方式来表示“创建一个调用Function.call 的函数,其中某个特定函数为this”。

    这正是bind 所做的。它说“获取一个函数并创建另一个函数,该函数使用特定的this 调用它”:

    function.bind(context)
    

    在我们的例子中,我们想要做的是“获取函数 Function.call 并创建另一个函数,用 this''.toLowerCase 调用它。这很简单

    Function.call.bind(''.toLowerCase)
    

    现在我们可以将其传递给map,而无需使用第二个参数:

    strings . map(Function.call.bind(''.toLowerCase))
    

    这与strings . map(Function.call, ''.toLowerCase) 完全相同,因为通常map(fn, ctxt) 正好等于map(fn.bind(ctxt))

    以下内容逐步分解为可读的形式:

    Function .           // From the Function object
      call .             // take the `call` method
      bind(              // and make a new function which calls it with a 'this' of
        ''.toLowerCase   // `toLowerCase`
      )
    

    当这个构造被指定为回调时,比如map,它的意思是:

    使用传入的第一个参数调用call,并将''.toLowerCase 作为this,根据call 的定义,这意味着调用toLowerCase 时将该参数作为this

    有些人喜欢通过说来简化这一点

    var call = Function.call;
    var toLowerCase = ''.toLowerCase;
    
    strings . map(call.bind(toLowerCase))
    

    或者,使用map提供的第二个参数,只是

    strings . map(call, toLowerCase)
    

    几乎可以用英语阅读:“ma​​p 每个字符串到 calling toLowerCase 的结果。

    另一个常见的相关用例是在承诺的then 中指定回调。考虑以下代码:

    promise . then(function(result) { result.frombulate(); })
    

    这很好,但有点冗长。并且then 在调用成功或失败处理程序时无法传递用作this 的上下文。但是有了上面的内容,我们现在可以写:

    promise . then(call.bind(frombulate))
    

    call.bind 惯用语还有其他用例,但这是最常见的用例之一:定义一个回调,其效果是调用某个函数,并将传递给回调的参数作为其 this

    有了ES6胖箭头函数,我当然可以写

    promise . then(result => result.frombulate())
    

    所以call.bind(frombulate)提供的简写优势相对较小,很难否认粗箭头版本比bind更易读。

    以下问题可能也很有趣:Array.map and lifted functions in Javascript

    【讨论】:

      【解决方案3】:
      m.call.bind(m)
      

      可以用作以下的简写:

      function(x){return m.bind(x)()}
      

      前者是后者的“无点”形式, 参数是隐含的。这将是有用的 像 map() 这样的列表操作,使它们更短。 你可以写这样的东西:

      let m   = "".toUpperCase;
      let fun = m.call.bind(m);
      let see = ['a','b'].map(fun);
      

      【讨论】: