forEach 原理
大约 2 分钟
forEach 原理
forEach() 方法是我们在处理数组使经常用到的遍历方法,它相较于其他循环方法可以使代码更加简洁,下面会探究 forEach() 方法的实现原理。
实现原理
ECMA (262) 标准中关于 forEach() 的实现步骤如下:
When the forEach method is called, the following steps are taken:
- Let O be ? ToObject(this value).
 - Let len be ? LengthOfArrayLike(O).
 - If IsCallable(callbackfn) is false, throw a TypeError exception.
 - Let k be 0.
 - Repeat, while k < len,
 
- Let Pk be ! ToString(𝔽(k)).
 - Let kPresent be ? HasProperty(O, Pk).
 - If kPresent is true, then
 - Let kValue be ? Get(O, Pk).
 - Perform ? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »).
 - Set k to k + 1.
 
- Return undefined.
 
具体实现
上面引用的一系列步骤详细说明了 forEach() 的实现标准,下面按照这个标准来实现一个 forEach() 方法。
具体实现代码如下:
Array.prototype.myForEach = function(callback){
    //第一步,获取 this 上下文,并将之转换为对象,这里由于 this 已经是对象了,所以跳过这个步骤
    //第二步,获取并保存数组的长度
    let len = this.length 
    
    //第三步,判断传入的回调函数是否是 function 类型,不是则抛出错误
    if(typeof callback !== 'function'){
      throw new TypeError(`${callback.name} is not a function`)
    }
    
    //第四步,设置一个循环变量 k,初始值为 0
    let k = 0
    
    //第五步,使用 while 循环,循环条件为 k < len(数组长度)
    while( k < len ){
       //将 k 转换为字符串类型,用 pk 保存
       let Pk = String(k)
       //这一步将数组看作对象,判断 Pk(数组索引)是否存在
       let kPresent = this.hasOwnProperty(Pk)
       //这一步判断如果 kPresent 存在,则继续执行
       if(kPresent){
          // 获取 Pk 索引对应的值
          let kValue = this[Pk]
          // 调用传入的回调方法,并传入数组当前项,数组当前索引,数组本身 参数
          callback.call(this, kValue, k, this)
       }
       // 循环变量增加
       k++
    }
    
    //第六步,返回 undefined
    return undefined
}
// 使用
const arr = [1,2,3,4,5]
arr.myForEach((item,index,self)=>{
   console.log(item)
   console.log(index)
   console.log(self)
})
// 输出:
//1
// 0
// (5) [1, 2, 3, 4, 5]
// 2
// 1
// (5) [1, 2, 3, 4, 5]
// 3
// 2
// (5) [1, 2, 3, 4, 5]
// 4
// 3
// (5) [1, 2, 3, 4, 5]
// 5
// 4
// (5) [1, 2, 3, 4, 5]
