谓词功能减低为什么需要包装的功能?

问题描述 投票:4回答:2

我玩弄Array.reduce与集之间的互动,我发现以下奇怪的行为。

通常情况下这个工程:

console.log(
  Set.prototype.add.call(new Set(), 1, 0, [])
);
// Set { 1 }

但是,如果我结合起来,与减少,以下不工作:

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对象为好。我想不出就是为什么是这种情况。

javascript node.js ecmascript-5
2个回答
1
投票

实际上有脚本的两个部分需要以正常工作的正确调用上下文(或this值)。第一部分,你已经想通了,是你需要调用Set.prototype.add与新创建Set的呼唤下,通过传递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需要与approprite调用上下文被调用。 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的调用上下文,而不是被保存在一个变量第一(这会失去调用上下文)。


4
投票

没有包装,你Set.prototype.add.call失去this值(至极应该Set.prototype.add功能,而是被设置为undefined)。

尝试这个:

[1,2,3].reduce(Set.prototype.add.call.bind(Set.prototype.add), new Set());

http://speakingjs.com/es5/ch01.html#_extracting_methods

© www.soinside.com 2019 - 2024. All rights reserved.