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

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

在日常的 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

探索JavaScript数组奥秘

avaScript数组同后端语言一样,具有它自己的数据结构,归根结底,这种数据结构,本质就是一种集合。在后端语言中(如java,.net等),数组是这样定义的:数组是用来存储相同数据类型的集合

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

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

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

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

数组、字符串去重

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

JavaScript 数组方法

数组方法:1、Array.join([param]) 方法:将数组中所有的元素都转换为字符串并连接起来,通过字符 param 连接,默认使用逗号,返回最后生成的字符串2、Array.reverse() 方法:将数组中的元素颠倒顺序(在原数组中重新排列它们),返回逆序数组

如何删除JavaScript 数组中的虚值

falsy(虚值)是在 Boolean 上下文中已认定可转换为‘假‘的值.JavaScript 在需要用到布尔类型值的上下文中使用强制类型转换(Type Conversion )将值转换为布尔值,比如:在条件语句或者循环语句中。

JavaScript中十种一步拷贝数组的方法

JavaScript中我们经常会遇到拷贝数组的场景,但是都有哪些方式能够来实现呢,我们不妨来梳理一下。扩展运算符(浅拷贝)自从ES6出现以来,这已经成为最流行的方法。

JS数组的几个经典api

本文主要来讲数组api的一些操作,如简单实现扁平化n维数组、数组去重、求数组最大值、数组求和、排序、对象和数组的转化等。扁平化嵌套数组/展平和阵列孔——flat()

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

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

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

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

点击更多...

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