跳至主要內容

flat 原理

wzCoding大约 2 分钟综合知识flat 原理

flat 原理

flat() 方法是我们在处理数组使经常用到的将多维数组展开的方法,它可以快速按照需要的展开层级将多维数组展开,下面会探究 flat() 方法的实现原理。

实现原理

ECMA (262) 标准中关于 flat() 的实现步骤如下:

When the flat method is called, the following steps are taken:

  1. Let O be ? ToObject(this value).
  2. Let sourceLen be ? LengthOfArrayLike(O).
  3. Let depthNum be 1.
  4. If depth is not undefined, then
  • Set depthNum to ? ToIntegerOrInfinity(depth).
  • If depthNum < 0, set depthNum to 0.
  1. Let A be ? ArraySpeciesCreate(O, 0).
  2. Perform ? FlattenIntoArray(A, O, sourceLen, 0, depthNum).
  3. Return A.

具体实现

上面引用的一系列步骤详细说明了 flat() 的实现标准,下面按照这个标准来实现一个 flat() 方法。

具体实现代码如下:

Array.prototype.myFlat = function(depth){
    //第一步,获取 this 上下文,并将之转换为对象,这里由于 this 已经是对象了,所以跳过这个步骤

    //第二步,获取并保存数组的长度
    let sourceLen = this.length 
    
    //第三步,定义 depthNum 变量,初始值为 1
    let depthNum = 1
    
    //第四步,如果传入的 depth 参数有值存在,将之赋值给 depthNum
    if(depth !== undefined){
       depthNum = +depth < 0 ? 0 : +depth
    }
    //第五步,创建一个数组用来保存展开的结果
    const result = []
    //第六步,这里的逻辑相当于 FlattenIntoArray 方法,具体执行对数组每一项展开的逻辑
    //浅拷贝数组
    const source = [...this]
    //循环遍历数组
    while(source.length > 0){
      //取出数组开头的元素,数组长度会减一
      let current = source.shift()
      //判断取出的元素是否是数组并且展开的深度是否大于1
      if(Array.isArray(current) && depth > 1){
         //将当前元素数组展开并添加到拷贝数组的开头
         source.unshift(...current)
         //展开后展开深度参数减一
         depth--
      }else{
         //向最终结果数组里面添加元素
         result.push(current)
      }
    }
    
    //第七步,返回 数组展开后的结果
    return result
}

// 使用
const arr = [1, [2, 3, [4, [5]]],{ a:1 }]
console.log(arr.myFlat(4))
// 输出:
// (6) [1, 2, 3, 4, 5, {a :1 }]
console.log(arr.myFlat(2))
// 输出:
// (6) [1, 2, 3, [4, [5]] {a :1 }]

还有另外一种更加简洁的实现方式,利用 Array.prototype.reduce() 方法实现,具体实现代码如下:

Array.prototype.myFlat = function (depth = 1) {
    //reduce方法会对数组的每一项执行传入的回调函数,回调函数的参数是上一次回调函数返回的
    //结果和当前元素
    return arr.reduce(function (flat, toFlatten) {
        return flat.concat((Array.isArray(toFlatten) && depth > 1) ? flat(toFlatten, depth - 1) : toFlatten);
    }, []);
}

// 使用
const arr = [1, [2, 3, [4, [5]]],{ a:1 }]
console.log(arr.myFlat(4))
// 输出:
// (6) [1, 2, 3, 4, 5, {a :1 }]
console.log(arr.myFlat(2))
// 输出:
// (6) [1, 2, 3, [4, [5]] {a :1 }]