为什么reduce中的predicate函数需要wrapper函数?
Why is the wrapper function required for the predicate function in reduce?
我正在尝试 Array.reduce 和 Set 之间的交互,我注意到以下奇怪的行为。
通常这有效:
console.log(
Set.prototype.add.call(new Set(), 1, 0, [])
);
// Set { 1 }
但如果我将其与 reduce 结合使用,以下内容将不起作用:
console.log(
[1,2,3].reduce(Set.prototype.add.call, new Set())
);
// TypeError: undefined is not a function
// at Array.reduce (<anonymous>)
但是,如果我将谓词函数包装在包装器中,这将起作用:
console.log(
[1,2,3].reduce((...args) => Set.prototype.add.call(...args), new Set())
);
// Set { 1, 2, 3 }
我在不同的 JS 引擎(Chrome 和 Safari)上进行了尝试并得到了相同的结果,因此它可能不是引擎特定的行为。这同样适用于 Map 对象。我想不通的是为什么会这样。
如果不换行,您的 Set.prototype.add.call
将失去其 this
值(应该是 Set.prototype.add
函数,但设置为 undefined
)。
试试这个:
[1,2,3].reduce(Set.prototype.add.call.bind(Set.prototype.add), new Set());
实际上脚本的 两个 部分需要正确的调用上下文(或 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');
上面代码片段中的错误有点误导。 methodCall
是一个函数——特别是Function.prototype.call
——它只是没有调用上下文,所以会抛出一个错误。此行为与以下代码段相同,其中 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
没有调用上下文。它类似于此答案中的第二个片段 - 就像 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
的调用上下文调用的,而不是先保存在变量中(这会丢失调用上下文)。
我正在尝试 Array.reduce 和 Set 之间的交互,我注意到以下奇怪的行为。
通常这有效:
console.log(
Set.prototype.add.call(new Set(), 1, 0, [])
);
// Set { 1 }
但如果我将其与 reduce 结合使用,以下内容将不起作用:
console.log(
[1,2,3].reduce(Set.prototype.add.call, new Set())
);
// TypeError: undefined is not a function
// at Array.reduce (<anonymous>)
但是,如果我将谓词函数包装在包装器中,这将起作用:
console.log(
[1,2,3].reduce((...args) => Set.prototype.add.call(...args), new Set())
);
// Set { 1, 2, 3 }
我在不同的 JS 引擎(Chrome 和 Safari)上进行了尝试并得到了相同的结果,因此它可能不是引擎特定的行为。这同样适用于 Map 对象。我想不通的是为什么会这样。
如果不换行,您的 Set.prototype.add.call
将失去其 this
值(应该是 Set.prototype.add
函数,但设置为 undefined
)。
试试这个:
[1,2,3].reduce(Set.prototype.add.call.bind(Set.prototype.add), new Set());
实际上脚本的 两个 部分需要正确的调用上下文(或 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');
上面代码片段中的错误有点误导。 methodCall
是一个函数——特别是Function.prototype.call
——它只是没有调用上下文,所以会抛出一个错误。此行为与以下代码段相同,其中 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
没有调用上下文。它类似于此答案中的第二个片段 - 就像 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
的调用上下文调用的,而不是先保存在变量中(这会丢失调用上下文)。