flat 原理
大约 2 分钟
flat 原理
flat()
方法是我们在处理数组使经常用到的将多维数组展开的方法,它可以快速按照需要的展开层级将多维数组展开,下面会探究 flat()
方法的实现原理。
实现原理
ECMA (262) 标准中关于 flat()
的实现步骤如下:
When the flat method is called, the following steps are taken:
- Let O be ? ToObject(this value).
- Let sourceLen be ? LengthOfArrayLike(O).
- Let depthNum be 1.
- If depth is not undefined, then
- Set depthNum to ? ToIntegerOrInfinity(depth).
- If depthNum < 0, set depthNum to 0.
- Let A be ? ArraySpeciesCreate(O, 0).
- Perform ? FlattenIntoArray(A, O, sourceLen, 0, depthNum).
- 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 }]