手把手教你手写 Array.prototype.forEach 方法实现

更新日期: 2025-06-10 阅读: 1k 标签: 数组

在日常的 JavaScript 开发中,array.forEach() 就像空气一样无处不在。但你是否好奇过这个简洁的遍历方法背后是如何运作的?今天,我们就来手写实现原生的 forEach 方法,彻底搞懂它的内在机制。


一、为何需要自己实现 forEach?

  1. 深入理解原理:避免成为只会调 api 的“API 工程师”

  2. 面试高频考点:手写数组方法是检验 JS 基础的重要标准

  3. 特殊场景定制:可扩展原生不支持的功能(如异步遍历)


二、手撕 forEach 源码(带详细注释)

Array.prototype.myForEach = function(callback, thisArg) {
  // 1. 安全检查:必须用函数调用
  if (this == null) {
    throw new TypeError('Array.prototype.myForEach called on null or undefined');
  }
  
  // 2. 回调必须是函数
  if (typeof callback !== 'function') {
    throw new TypeError(callback + ' is not a function');
  }
  
  // 3. 转换类数组对象(如 arguments)
  const array = Object(this);
  const length = array.length >>> 0;  // 确保为整数
  
  let index = 0;
  // 4. 核心遍历逻辑
  while (index < length) {
    // 跳过空位(保持与原生一致行为)
    if (index in array) {
      // 关键步骤:执行回调并绑定this
      callback.call(thisArg, array[index], index, array);
    }
    index++;
  }
};


三、关键实现细节解析

  1. 稀疏数组处理:使用 index in array 检测有效索引,跳过 empty 项

  2. 安全类型转换:Object(this) 确保字符串等类数组可用

  3. 绑定执行上下文:callback.call(thisArg, ...) 动态绑定 this

  4. 长度处理技巧:length >>> 0 将非数字转为0,避免非法长度


四、实测对比原生方法

const arr = [1, 2, , 4]; // 含空位的数组

// 原生行为
arr.forEach(item => console.log(item)); // 输出: 1, 2, 4

// 我们的实现
arr.myForEach(item => console.log(item)); // 输出: 1, 2, 4 ✅


五、超越原生的扩展能力

理解原理后,我们可以扩展更多实用功能:

// 支持异步回调的 forEach
Array.prototype.asyncForEach = async function(callback) {
  for (let i = 0; i < this.length; i++) {
    await callback(this[i], i, this);
  }
};

// 带中断机制的 forEach
Array.prototype.breakableForEach = function(callback) {
  for (let i = 0; i < this.length; i++) {
    if (callback(this[i], i, this) === false) break;
  }
};
重要提示:生产环境务必使用原生方法,手写实现主要用于学习原理。修改内置原型(如 Array.prototype)可能引发不可预见的冲突。


六、为什么面试官爱考这个?

  1. 考察对原型链的理解:函数挂载到 Array.prototype

  2. 检验参数处理能力:类型检查、this 绑定

  3. 验证边界处理:稀疏数组、非法调用等场景

  4. 评估代码健壮性:防御式编程的实践

动手实践建议:在实现后,尝试用相同思路手写 map、filter 或 reduce 方法,你会惊讶于自己的进步速度。

理解 forEach 的底层实现,不仅能让你在面试中游刃有余,更重要的是培养透视语言特性本质的能力。下次看到数组方法时,你看到的将不再是黑盒API,而是可拆解、可定制的代码逻辑——这才是真正的高级前端工程师思维。

本文深入探讨了 forEach 的 6 大实现要点,包含 3 种扩展方案,实测代码可直接运行。掌握这些核心原理,将使你在 JavaScript 底层机制的理解上超越 90% 的开发者。


本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/12766

相关推荐

indexOf的三种使用方法

indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。这里基本用法大家一般都清楚,一般在实际工作中常与数组的方法合用来对数组进行一些操作

关于Vue不能监听(watch)数组变化

vue无法监听数组变化的情况,但是数组在下面两种情况下无法监听:利用索引直接设置数组项时,例如arr[indexofitem]=newValue;修改数组的长度时,例如arr.length=newLength

JS计算两个数组的交集、差集、并集、补集(多种实现方式)

使用 ES5 语法来实现虽然会麻烦些,但兼容性最好,不用考虑浏览器 JavaScript 版本,使用 ES5 语法来实现虽然会麻烦些,但兼容性最好,不用考虑浏览器 JavaScript 版本。也不用引入其他第三方库。

js数组中改变元素的位置:互换,置顶,上移,下移

unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。splice() 方法可删除从 index 处开始的零个或多个元素

js使用数组+循环+条件实现数字转换为汉字的简单方法。

单个数字转汉字的解决方法:利用数组存储0-9的汉字、 ary.length和str.length不用多说,这是指ary数组和str字符串的长度。这里我们需要注意的是str.charAt(j)和ary[i],分别指在str这个字符串中索引为j的元素,在ary中索引为i的元素。

Js遍历数组时注意 Empty Item 的影响

这两天碰到个问题:从日志中发现一些来自 iOS 10.3 的报错「Cannot read property \\\'xxx\\\' of undefined」,定位到代码的报错位置,发现是遍历某数组时产生的报错,该数组的元素应该全都是 Object,但实际上出现了异常的元素

JS数组扁平化(flat)方法总结

需求:多维数组=>一维数组 ;flat和flatMap方法为ES2019(ES10)方法,目前还未在所有浏览器完全兼容。第四种处理:用 reduce 实现数组的 flat 方法

数组、字符串去重

今天说的数组和字符串去重呢,主要用到es6新的数据结构 Set,它类似于数组,但是成员的值都是唯一的,没有重复的值,所以活用Set来进行数组和字符串的去重。

Js数组中所有方法(超详细)

concat()把元素衔接到数组中。 every() 方法使用指定函数检测数组中的所有元素:filter()返回满足断言函数的数组元素。forEach()为数组的每一个元素调用指定函数。

[译]async-await 数组循环的几个坑

在 Javascript 循环中使用 async/ await 循环遍历数组似乎很简单,但是在将两者结合使用时需要注意一些非直观的行为。让我们看看三个不同的例子,看看你应该注意什么,以及哪个循环最适合特定用例。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!