.map() 等高阶函数如何在 JavaScript 内部工作?
How do higher-order functions, like .map(), work internally in JavaScript?
现在每个人都尝试使用这种 higher-order functions 来获得有希望的结果,同时编写更少的代码。但是我想知道这些函数在内部是如何工作的。
假设我写这样的东西
var numbers = [16, 25, 36];
var results = numbers.map(Math.sqrt);
console.log(results); // [4, 5, 6]
我知道 'number' 数组的每个元素都在逐个迭代,但是 how?
我试图搜索它,但我还没有得到满意的答案。
.map
只是一个接受回调的方法,为数组的每个项目调用回调,并将值分配给新数组。没什么特别的。您甚至可以很容易地自己实现它:
Array.prototype.myMap = function(callback) {
const newArr = [];
for (let i = 0; i < this.length; i++) {
newArr.push(callback(this[i], i, this));
}
return newArr;
}
var numbers = [16, 25, 36];
var results = numbers.myMap(Math.sqrt);
console.log(results); // [4, 5, 6]
为了完全符合规范,您需要 also need 检查 this
是一个对象,callback
是可调用的,以及.call
带有第二个参数的回调传递给 myMap
如果有的话,但这些细节对于开始理解高阶函数并不重要。
我想每个供应商都应该按照 spec
实施它
实际实现,例如 V8 可能有点复杂,请参考 this answer 开始。您也可以参考github中的v8源码,但孤立地只看一部分可能不太容易。
引用上面的回答:
V8 developer here. We have several different implementation techniques
for "builtins": some are written in C++, some in Torque, some in what
we call CodeStubAssembler, and a few directly in assembly. In earlier
versions of V8, some were implemented in JavaScript. Each of these
strategies has its own strengths (trading off code complexity,
debuggability, performance in various situations, binary size, and
memory consumption); plus there is always the historical reason that
code has evolved over time.
ES2015 规范:
- 设O为ToObject(this value).
- ReturnIfAbrupt(O).
- 设 len 为 ToLength(Get(O,
"length"
)).
- ReturnIfAbrupt(len).
- 如果 IsCallable(callbackfn) 为 false,抛出一个 TypeError 异常。
- 如果提供了 thisArg,则让 T 为 thisArg;否则让 T 为 undefined.
- 设A为ArraySpeciesCreate(O, len).
- ReturnIfAbrupt(A).
- 设k为0。
- 重复,同时 k < len
- 设 Pk 为 ToString(k).
- 设kPresent为HasProperty(O,Pk).
- ReturnIfAbrupt(kPresent).
- 如果 kPresent 为 true,则
- 设kValue为Get(O,Pk).
- ReturnIfAbrupt(kValue).
- 设 mappedValue 为 Call(callbackfn, T, « kValue, k, O»).
- ReturnIfAbrupt(mappedValue).
- 设status为CreateDataPropertyOrThrow (A, Pk, mappedValue ).
- ReturnIfAbrupt(状态).
- 将 k 增加 1。
- ReturnA.
现在每个人都尝试使用这种 higher-order functions 来获得有希望的结果,同时编写更少的代码。但是我想知道这些函数在内部是如何工作的。
假设我写这样的东西
var numbers = [16, 25, 36];
var results = numbers.map(Math.sqrt);
console.log(results); // [4, 5, 6]
我知道 'number' 数组的每个元素都在逐个迭代,但是 how?
我试图搜索它,但我还没有得到满意的答案。
.map
只是一个接受回调的方法,为数组的每个项目调用回调,并将值分配给新数组。没什么特别的。您甚至可以很容易地自己实现它:
Array.prototype.myMap = function(callback) {
const newArr = [];
for (let i = 0; i < this.length; i++) {
newArr.push(callback(this[i], i, this));
}
return newArr;
}
var numbers = [16, 25, 36];
var results = numbers.myMap(Math.sqrt);
console.log(results); // [4, 5, 6]
为了完全符合规范,您需要 also need 检查 this
是一个对象,callback
是可调用的,以及.call
带有第二个参数的回调传递给 myMap
如果有的话,但这些细节对于开始理解高阶函数并不重要。
我想每个供应商都应该按照 spec
实施它实际实现,例如 V8 可能有点复杂,请参考 this answer 开始。您也可以参考github中的v8源码,但孤立地只看一部分可能不太容易。
引用上面的回答:
V8 developer here. We have several different implementation techniques for "builtins": some are written in C++, some in Torque, some in what we call CodeStubAssembler, and a few directly in assembly. In earlier versions of V8, some were implemented in JavaScript. Each of these strategies has its own strengths (trading off code complexity, debuggability, performance in various situations, binary size, and memory consumption); plus there is always the historical reason that code has evolved over time.
ES2015 规范:
- 设O为ToObject(this value).
- ReturnIfAbrupt(O).
- 设 len 为 ToLength(Get(O,
"length"
)). - ReturnIfAbrupt(len).
- 如果 IsCallable(callbackfn) 为 false,抛出一个 TypeError 异常。
- 如果提供了 thisArg,则让 T 为 thisArg;否则让 T 为 undefined.
- 设A为ArraySpeciesCreate(O, len).
- ReturnIfAbrupt(A).
- 设k为0。
- 重复,同时 k < len
- 设 Pk 为 ToString(k).
- 设kPresent为HasProperty(O,Pk).
- ReturnIfAbrupt(kPresent).
- 如果 kPresent 为 true,则
- 设kValue为Get(O,Pk).
- ReturnIfAbrupt(kValue).
- 设 mappedValue 为 Call(callbackfn, T, « kValue, k, O»).
- ReturnIfAbrupt(mappedValue).
- 设status为CreateDataPropertyOrThrow (A, Pk, mappedValue ).
- ReturnIfAbrupt(状态).
- 将 k 增加 1。
- ReturnA.