实际上有 两个 部分的脚本需要正确的调用上下文(或this 值)才能正常工作。您已经知道的第一部分是,您需要使用新创建的Set 的调用上下文调用Set.prototype.add,将Set 作为第一个参数传递给.call:
// works:
Set.prototype.add.call(new Set(), 1, 0, []);
// works, args[0] is the new Set:
[1,2,3].reduce((..args) => Set.prototype.add.call(..args), new Set());
但另一个问题是 .call 需要使用适当的调用上下文来调用。 Set.prototype.add.call与Function.prototype.call指的功能相同:
console.log(Set.prototype.add.call === Function.prototype.call);
Function.prototype.call 调用的函数基于其调用上下文。例如
someObject.someMethod.call(< args >)
函数的调用上下文是函数调用中最后一个 . 之前的所有内容。因此,对于上述情况,.call 的调用上下文是someObject.someMethod。这就是.call 知道要运行哪个函数的方式。如果没有调用上下文,.call 将无法工作:
const obj = {
method(arg) {
console.log('method running ' + arg);
}
};
// Works, because `.call` has a calling context of `obj.method`:
obj.method.call(['foo'], 'bar');
const methodCall = obj.method.call;
// Doesn't work, because methodCall is being called without a calling context:
methodCall(['foo'], 'bar');
上面的 sn-p 中的错误有点误导。 methodCall 是 一个函数 - 特别是 Function.prototype.call - 它只是没有调用上下文,因此会引发错误。此行为与以下 sn-p 相同,其中 Function.prototype.call 在没有调用上下文的情况下被调用:
console.log(typeof Function.prototype.call.call);
Function.prototype.call.call(
undefined,
);
希望这应该清楚地表明,在使用 .call 时,您需要在正确的调用上下文中使用它,否则它将失败。所以,回到原来的问题:
[1,2,3].reduce(Set.prototype.add.call, new Set());
失败,因为 reduce 的内部调用 Set.prototype.add.call 没有调用上下文。它类似于此答案中的第二个 sn-p - 就像将 Set.prototype.add.call 放入独立变量中,然后调用。
// essential behavior of the below function is identical to Array.prototype.reduce:
Array.prototype.customReduce = function(callback, initialValue) {
let accum = initialValue;
for (let i = 0; i < this.length; i++) {
accum = callback(accum, this[i]);
// note: "callback" above is being called without a calling context
}
return accum;
};
// demonstration that the function works like reduce:
// sum:
console.log(
[1, 2, 3].customReduce((a, b) => a + b, 0)
);
// multiply:
console.log(
[1, 2, 3, 4].customReduce((a, b) => a * b, 1)
);
// your working Set code:
console.log(
[1,2,3].customReduce((...args) => Set.prototype.add.call(...args), new Set())
);
// but because "callback" isn't being called with a calling context, the following fails
// for the same reason that your original code with "reduce" fails:
[1,2,3].customReduce(Set.prototype.add.call, new Set());
相比之下,
(..args) => Set.prototype.add.call(..args)
有效(在.reduce 和.customReduce 中),因为.call 是使用Set.prototype.add 的调用上下文调用的,而不是先保存在变量中(这会丢失调用上下文)。