在使用 forEach 时的一些现象

  1. 为什么次添加一个元素到数组不会陷入死循环?
const arr = [1, 2, 3];
arr.forEach((item) => {
arr.push("x");
console.log("arr", item); //arr 1 arr 2 arr 3
});
  1. 为什么下标为 1 的元素没有删除?
const arr = [1, 2, 3];
arr.forEach((item, i) => {
arr.splice(i, 1);
console.log("arr", item); //arr 1 arr 3
});
console.log(arr); //[2]
  1. 为什么存在稀疏元素时 会跳过稀疏数组?
const arr = [, , 3];
arr.forEach((item) => {
console.log("arr", item); // arr 3
});

解答

  1. 首先深入了解一下 forEach 的实现(图片引用自MDN
    forEach

  2. ecma2023版 forEach 实现流程
    forEach2023

  3. forEach 的实现

const arr = [1, 2, 3];

Array.prototype.forEach = function (callback) {
//Let O be ? ToObject(this value)-->this 指向当前的数组
// Let len be ? LengthOfArrayLike(O).--> 确定数组的长度
const len = this.length;
//If IsCallable(callbackfn) is false, throw a TypeError exception-> 判断callback是否为函数
if (typeof callback !== "function") {
throw new TypeError(callback + "is not a function");
}
//Let k be 0 --> 定义k的初始值 感觉是下标
let k = 0;
//Repeat, while k < len -->开始迭代
while (k < len) {
//Let kPresent be ? HasProperty(O, Pk).判断当前下标是否在数组中
//这里就是跳过稀疏元素的原因
if (k in this) {
callback(this[k], k, this); // 执行回调函数
}
//Increase k by 1. -->指向下一个元素
k++;
}
};

arr.forEach((item) => {
console.log("arr", item); // arr 3
});

console.log(arr); // [2]
  1. 为什么元素 2 无法够删除成功?
  • 第一次执行时,k=0,k in this 返回 true,执行 callback,删除下标为 0 的 1,k++,k=1,arr=[2,3]
  • 第二次执行时,k=1,k in this 返回 true,执行 callback,删除下标为 1 的三,k++,k=2 arr=[2]
  • 第三次执行时,k=2,此时数组中已不存在下标为 2 的元素 k in this 返回 false,跳出循环
  1. 为什么不会陷入死循环?
  • 因为刚开始循环数组时 数组的长度已经确定了
  • 在当前作用域不论你怎么修改数组的长度也无法影响 forEach 的执行