好问题。让我们从之前在 * 上出现的一个示例开始:将数组中的所有字符串映射为小写。当然可以写
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)
人们已经尝试过了,然后想知道为什么它不起作用。原因是即使我们将call 或toLowerCase 作为回调传递给map,map 仍然不知道应该使用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)
几乎可以用英语阅读:“map 每个字符串到 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。