Array.prototype.map
map 对数组每个元素执行回调,返回新数组。回调接收 (value, index, array) 三个参数。
#type / concept
#status / evergreen
#resource / javascript
#resource / ecmascript
[!info] related notes
- 相关概念: ecmascript-collection-reference-types, higher-order-function-callback-mismatch, parseint
- 对比方法:
forEach,filter,reduce
Array.prototype.map
一句话定义
map 对数组每个元素执行回调函数,将回调的返回值组成新数组返回,不修改原数组。
签名
array.map(callbackFn)
array.map(callbackFn, thisArg)
callbackFn 参数
这是最容易被忽略的关键点。 回调函数会收到 3 个参数,而不是只有值:
array.map(function callback(currentValue, index, array) {
// currentValue : 当前元素
// index : 当前索引
// array : 原数组本身
});
| 参数 | 含义 |
|---|---|
currentValue | 当前正在处理的元素 |
index | 当前元素的索引 |
array | 调用 map 的数组本身 |
执行逻辑
- 创建一个与原数组等长的新数组
- 按索引从
0到length - 1依次遍历 - 对每个已赋值的索引位置,调用回调函数
- 将回调的返回值填入新数组的对应位置
- 跳过空位(sparse array 的 empty slot)
const arr = [1, , 3];
arr.map(x => x * 2); // [2, empty, 6] ← 空位被跳过
对比显式 undefined:
const arr = [1, undefined, 3];
arr.map(x => x * 2); // [2, NaN, 6] ← undefined 会被处理
基本用法
[1, 2, 3].map(x => x * 2) // [2, 4, 6]
['a', 'b', 'c'].map(s => s.toUpperCase()) // ['A', 'B', 'C']
[1, 2, 3].map((x, i) => `${i}: ${x}`) // ['0: 1', '1: 2', '2: 3']
返回值
返回一个新数组,长度与原数组相同(除非有空位)。
const original = [1, 2, 3];
const mapped = original.map(x => x * 2);
console.log(mapped); // [2, 4, 6]
console.log(original); // [1, 2, 3](不变)
thisArg
可选的第二个参数,指定回调中 this 的值:
const obj = { factor: 10 };
[1, 2, 3].map(function(x) {
return x * this.factor;
}, obj); // [10, 20, 30]
注意:箭头函数有自己的 this,thisArg 对箭头函数无效。
经典陷阱:map(parseInt)
详见 higher-order-function-callback-mismatch。
['1', '2', '3'].map(parseInt)
// 实际执行:parseInt('1', 0), parseInt('2', 1), parseInt('3', 2)
// 结果:[1, NaN, NaN]
原因:map 会传 (value, index),而 parseInt 的第二参数是 radix(进制),index=1 是非法进制。
与相关方法对比
| 方法 | 返回值 | 跳过空位 | 修改原数组 |
|---|---|---|---|
map | 新数组 | 是 | 否 |
forEach | undefined | 是 | 否(但回调里可以改) |
filter | 新数组(子集) | 是 | 否 |
reduce | 累积值 | 是 | 否 |