Array.prototype.map()
map() 方法创建一个新数组,其结果是原数组中的每个元素都调用一个提供的函数后返回的结果。 map() 方法不会改变原数组,然而接受的回调函数可以改变原数组。
语法
map(callbackFn)
map(callbackFn, thisArg)
参数
map 方法接受两个参数:回调函数 和 指定回调函数中的 this
值。
1、callbackFn:为数组中的每个元素执行的函数。它的返回值作为一个元素被添加到新数组中(是添加不是追加)
。
该函数被调用时将传入以下参数:
- currentValue:数组中当前正在处理的元素。
- index:正在处理的元素在数组中的索引。
- array:调用了 map() 的数组本身。
2、thisArg(可选):执行 callbackFn 时用作 this 的值。
返回值
一个新数组,每个元素都是回调函数的返回值。
用法
第一种(推荐)
const array = [1, 2, 3]
const newArray = array.map((currentValue, index, array) => {
// 返回新数组中的元素值
return currentValue + 1
})
// [2, 3, 4]
第二种(基于 this 进行操作时)
const thisArg = { name: 'Aimilali' }
const array = [1, 2, 3]
const newArray = array.map(function (currentValue, index, array) {
// 返回新数组中的元素值
return this.name + currentValue
}, thisArg)
// ['Aimilali1', 'Aimilali2', 'Aimilali3']
描述
map() 方法是一个迭代方法。它为数组中的每个元素调用一次提供的 callbackFn 函数,并用结果构建一个新数组。
callbackFn 仅在已分配值的数组索引处被调用。它不会在稀疏数组中的空槽处被调用
。
稀疏数组使用 map() 方法后返回值仍然是稀疏数组。空槽的索引在返回的数组中仍然为空,并且回调函数不会对它们进行调用。
请注意,在第一次调用 callbackFn之前,数组的长度已经被保存
。因此:
- 当开始调用 map() 时,callbackFn 将不会访问超出数组初始长度的任何元素。
- 对已访问索引的更改不会导致再次在这些元素上调用 callbackFn。
- 如果数组中一个现有的、尚未访问的元素被 callbackFn 更改,则它传递给 callbackFn 的值将是该元素被修改后的值。
被删除的元素则不会被访问
。
上述类型的并发修改经常导致难以理解的代码,通常应避免(特殊情况除外)。
map() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。
由于 map 创建一个新数组,在不使用 map 返回的新数组的情况下不推荐使用 map。应该使用 forEach 或 for...of 作为代替。
实现 map 方法
从上面 map 描述总结实现 map 时注意实现这三点。
- 回调函数调用之前,数组的长度已经被保存。
- 回调函数不会在稀疏数组中的空槽处被调用。
- 稀疏数组使用 map() 方法后返回值仍然是稀疏数组。
Array.prototype.myMap = function (fun, context) {
if (!Object.is(typeof fun, 'function')) {
throw TypeError(`${typeof fun} is not a function`)
}
const length = this.length // 保存数组长度
const newArr = Array(length)
for (let i = 0; i < length; i++) {
if (Object.hasOwn(this, i)) { // 跳过稀疏数组
newArr[i] = fun.call(context, this[i], i, this) // 确保返回值仍然是稀疏数组
}
}
return newArr
}
测试用例
Array.prototype.myMap = function (fun, context) {
if (!Object.is(typeof fun, 'function')) {
throw TypeError(`${typeof fun} is not a function`)
}
const length = this.length // 保存数组长度
const newArr = Array(length)
for (let i = 0; i < length; i++) {
if (Object.hasOwn(this, i)) { // 跳过稀疏数组
newArr[i] = fun.call(context, this[i], i, this) // 确保返回值仍然是稀疏数组
}
}
return newArr
}
console.log('给每个数组元素加 1')
const arr = [1, 2, 3, 4, 5]
const resultMyMap = arr.myMap((num) => num + 1)
const resultMap = arr.map((num) => num + 1)
console.log(resultMyMap)
console.log(resultMap)
// [2, 3, 4, 5, 6]
console.log('遍历稀疏数组')
const arr1 = [1, , 3, , 5]
const resultMyMap1 = arr1.myMap((num) => num + 1)
const resultMap1 = arr1.map((num) => num + 1)
console.log(resultMyMap1)
console.log(resultMap1)
// [2, empty, 4, empty, 6]
console.log('非数组对象上使用 map')
const arrayLike = {
length: 3,
0: 2,
1: 3,
2: 4,
}
console.log(Array.prototype.myMap.call(arrayLike, (x) => x * 2))
console.log(Array.prototype.map.call(arrayLike, (x) => x * 2))
// [4, 6, 8]
console.log('跟 parseInt() 一起使用')
const arr2 = ["1", "2", "3"]
const resultMyMap2 = arr2.myMap(parseInt)
const resultMap2 = arr2.map(parseInt)
console.log(resultMyMap2)
console.log(resultMap2)
// [1, NaN, NaN]
console.log('需要二个参数时')
const arr3 = [1, 2, 3]
const obj = { name: 'Aimilali' }
const resultMyMap3 = arr3.myMap(function (value) {
return this.name + value
}, obj)
const resultMap3 = arr3.map(function (value) {
return this.name + value
}, obj)
console.log(resultMyMap3)
console.log(resultMap3)
// ['Aimilali1', 'Aimilali2', 'Aimilali3']
console.log('测试回调函数所有参数')
const arr4 = [1]
arr4.myMap((value, index, currentArr) => console.log(value, index, currentArr))
arr4.map((value, index, currentArr) => console.log(value, index, currentArr))
// 1 0 [1]
console.log('map 传递的第一个参数不是函数时')
const arr5 = [1]
try {
arr5.myMap()
} catch (err) {
console.error(err)
}
try {
arr5.map()
} catch (err) {
console.error(err)
}
// Uncaught TypeError: undefined is not a function
结语
到这里 Array 实例方法 map 实现完成啦。
JavaScript 中的 Array 类型提供了一系列强大的实例方法。在 Array 实例方法实现系列
专栏中,我将深入探讨一些常见的 Array 实例方法,解析它们的实现原理。
如果有错误或者不严谨的地方,请大家务必给予指正,十分感谢。欢迎大家在评论区中讨论。