手写实现 Array.prototype.map 方法
理解并手动实现 JavaScript 数组的 map 方法,掌握数组遍历和回调函数的原理
问题
Array.prototype.map() 是 JavaScript 中常用的数组方法,它会创建一个新数组,其结果是该数组中的每个元素都调用一次提供的函数后的返回值。本题要求手动实现一个功能完整的 map 方法,理解其内部实现原理。
解答
/**
* 手写实现 Array.prototype.map 方法
* @param {Function} callback - 回调函数,接收三个参数:当前元素、索引、原数组
* @param {*} thisArg - 执行 callback 时使用的 this 值
* @returns {Array} 返回一个新数组
*/
Array.prototype.myMap = function(callback, thisArg) {
// 1. 处理异常情况
if (this == null) {
throw new TypeError('Array.prototype.myMap called on null or undefined');
}
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
// 2. 将调用对象转换为对象
const O = Object(this);
// 3. 获取数组长度(无符号右移保证为非负整数)
const len = O.length >>> 0;
// 4. 创建新数组用于存储结果
const result = new Array(len);
// 5. 遍历数组,执行回调函数
for (let i = 0; i < len; i++) {
// 只处理数组中存在的元素(跳过空位)
if (i in O) {
// 调用回调函数,传入当前元素、索引、原数组,并绑定 this
result[i] = callback.call(thisArg, O[i], i, O);
}
}
// 6. 返回新数组
return result;
};
使用示例
// 示例1:基本使用
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.myMap(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// 示例2:使用索引参数
const arr = ['a', 'b', 'c'];
const withIndex = arr.myMap((item, index) => `${index}-${item}`);
console.log(withIndex); // ['0-a', '1-b', '2-c']
// 示例3:指定 this 上下文
const multiplier = {
factor: 10,
multiply: function(num) {
return num * this.factor;
}
};
const nums = [1, 2, 3];
const result = nums.myMap(function(num) {
return this.multiply(num);
}, multiplier);
console.log(result); // [10, 20, 30]
// 示例4:处理稀疏数组
const sparse = [1, , 3]; // 中间有空位
const mapped = sparse.myMap(x => x * 2);
console.log(mapped); // [2, empty, 6]
console.log(mapped.length); // 3
// 示例5:对象数组转换
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: 35 }
];
const names = users.myMap(user => user.name);
console.log(names); // ['Alice', 'Bob', 'Charlie']
关键点
-
类型检查:需要检查
this是否为null/undefined,以及callback是否为函数,不符合则抛出TypeError -
对象转换:使用
Object(this)将调用对象转换为对象类型,确保可以正常访问属性 -
长度处理:使用无符号右移
>>> 0确保长度为非负整数,这是 ECMAScript 规范的标准做法 -
创建新数组:map 方法不会修改原数组,而是返回一个新数组,长度与原数组相同
-
稀疏数组处理:使用
in操作符检查索引是否存在,跳过数组中的空位(empty slots) -
this 绑定:使用
call方法调用回调函数,正确绑定thisArg参数作为回调函数的this值 -
回调参数:回调函数接收三个参数:当前元素值、当前索引、原数组对象
-
返回值:每次回调函数的返回值会作为新数组对应位置的元素
目录