手写实现 Array.prototype.forEach 方法
理解并实现 JavaScript 数组的 forEach 方法,掌握数组遍历的原理
问题
forEach 是 JavaScript 数组的一个常用方法,用于遍历数组中的每个元素并执行回调函数。我们需要手动实现一个功能完整的 forEach 方法,理解其内部工作原理,包括:
- 遍历数组的每个元素
- 为回调函数传递正确的参数(当前元素、索引、原数组)
- 正确处理
this指向 - 处理稀疏数组(跳过空位)
- 返回
undefined
解答
/**
* 手写实现 forEach 方法
* @param {Function} callback - 回调函数,接收三个参数:当前元素、索引、原数组
* @param {*} thisArg - 可选参数,执行回调时的 this 值
*/
Array.prototype.myForEach = function(callback, thisArg) {
// 1. 检查调用对象是否为 null 或 undefined
if (this == null) {
throw new TypeError('Array.prototype.myForEach called on null or undefined');
}
// 2. 检查 callback 是否为函数
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
// 3. 将调用对象转换为对象
const O = Object(this);
// 4. 获取数组长度,使用无符号右移确保为正整数
const len = O.length >>> 0;
// 5. 遍历数组
for (let i = 0; i < len; i++) {
// 6. 只处理数组中实际存在的元素(跳过稀疏数组的空位)
if (i in O) {
// 7. 调用回调函数,传入三个参数,并绑定 this
callback.call(thisArg, O[i], i, O);
}
}
// 8. forEach 方法没有返回值(返回 undefined)
return undefined;
};
使用示例
// 示例 1: 基本使用
const arr1 = [1, 2, 3, 4, 5];
arr1.myForEach((item, index) => {
console.log(`索引 ${index}: ${item}`);
});
// 输出:
// 索引 0: 1
// 索引 1: 2
// 索引 2: 3
// 索引 3: 4
// 索引 4: 5
// 示例 2: 使用 thisArg 参数
const obj = {
multiplier: 2,
multiply: function(arr) {
arr.myForEach(function(item) {
console.log(item * this.multiplier);
}, this); // 传入 this 作为 thisArg
}
};
obj.multiply([1, 2, 3]);
// 输出: 2, 4, 6
// 示例 3: 处理稀疏数组
const arr2 = [1, , 3, , 5]; // 包含空位的稀疏数组
arr2.myForEach((item, index) => {
console.log(`索引 ${index}: ${item}`);
});
// 输出:
// 索引 0: 1
// 索引 2: 3
// 索引 4: 5
// 注意:索引 1 和 3 被跳过
// 示例 4: 修改原数组
const arr3 = [1, 2, 3];
arr3.myForEach((item, index, array) => {
array[index] = item * 2;
});
console.log(arr3); // [2, 4, 6]
// 示例 5: 对比原生 forEach
const arr4 = ['a', 'b', 'c'];
console.log(arr4.myForEach(item => console.log(item))); // undefined
console.log(arr4.forEach(item => console.log(item))); // undefined
关键点
-
参数校验:需要检查调用对象是否为
null/undefined,以及callback是否为函数类型 -
this 绑定:使用
callback.call(thisArg, ...)来正确绑定回调函数的this指向 -
稀疏数组处理:使用
in操作符检查索引是否存在,跳过数组中的空位(empty slots) -
长度处理:使用
>>> 0无符号右移运算符确保长度为非负整数 -
回调参数:回调函数接收三个参数:当前元素值、当前索引、原数组对象
-
返回值:
forEach方法始终返回undefined,不支持链式调用 -
不可中断:
forEach无法通过break或return提前终止循环(这是与for循环的重要区别) -
类型转换:使用
Object(this)将调用对象转换为对象类型,确保兼容性
目录