前端百题斩【020】——竟然有五种方式实现flat方法

写该系列文章的初衷是“让每位前端工程师掌握高频知识点,为工作助力”。这是前端百题斩的第20斩,希望朋友们关注公众号“执鸢者”,用知识武装自己的头脑。

20.1 背景

不知道老铁们有没有遇到过一道面试题:如何将一个多维数组展开成一个一维数组?当时我遇到的时候还不了解flat这个神奇的方法,用了最传统的解决方法进行解决。

const flatten = arr => arr.toString().split(',').map(item => +item);

const arr = [1, 2, [3, 4, [5, 6]]];
console.log(flatten(arr)); // [ 1, 2, 3, 4, 5, 6 ]

上述方法是不是很神奇,会将多层级的数组展开成为一个层级,但是该方式其实存在很大问题的,下面让我们一起看看这些问题。

  1. 不管多少层级都会展开为一个层级;

  2. 处理后的结果其实都是字符串,需要后续再转换为原来的类型。

正是基于这个契机,发现了ES6新增了flat函数,这个函数天生就是为数据扁平化处理而生的。

20.2 flat基础

flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

  1. flat方法的用法如下所示:

const newArray = arr.flat([depth])
  1. 小试牛刀

const arr = [1, 2, [3, 4, [5, 6]]];
console.log(arr.flat(1)); // [ 1, 2, 3, 4, [ 5, 6 ] ]
console.log(arr.flat(2)); // [ 1, 2, 3, 4, 5, 6 ]

20.3 实现

flat这么香,那么我们是否可以自己实现一个呢?实现该方法的方式有很多,下面就让我们一起看看这五种方式。(注:这五种方式试MDN上给出的替代方案)

20.3.1 使用reduce和concat

该方式实现起来虽然很简单,但是存在一个很大的缺陷:只能展开一层,对于多层的情况将无能为力。其思想总结起来为以下两个步骤:

  1. 利用reduce函数去依次处理每个数组中的元素;

  2. 利用concat将当前的数组元素(值或子数组)添加到结果数组中。

// 使用reduce和concat
Array.prototype.flat1 = function () {
    return this.reduce((acc, val) => acc.concat(val), []);
}
20.3.2 使用reduce + concat + isArray + recursivity

该方式已经具备展开多层的能力了,其实现思想可总结为以下几点:

  1. 利用reduce函数去依次处理每个数组中的元素;

  2. 利用concat将当前元素添加到结果数组中;

  3. 利用isArray判断当前数组中的元素是不是一个数组;

  4. 利用递归思想展开多层级的数组。

// 使用reduce + concat + isArray +recursivity
Array.prototype.flat2 = function (deep = 1) {
    const flatDeep = (arr, deep = 1) => {
        return deep > 0 ? arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flatDeep(val, deep - 1) : val), []) : arr.slice();
    }

    return flatDeep(this, deep);
}
20.3.3 使用forEach + concat + isArray +recursivity

该方式与上述方式很类似,能够设定层级展开,只是遍历数组由reduce转换为forEach。

// 使用forEach + concat + isArray +recursivity
// forEach 遍历数组会自动跳过空元素
Array.prototype.flat3 = function (deep = 1) {
    const result = [];
    (function flat(arr, deep) {
        arr.forEach((item) => {
            if (Array.isArray(item) && deep > 0) {
                flat(item, deep - 1);
            } else {
                result.push(item);
            }
        })
    })(this, deep);

    return result;
}
20.3.4 使用for of + concat + isArray +recursivity

该方式与上述方式很类似,能够设定层级展开,只是遍历数组利用了for of方法

// 使用for of + concat + isArray +recursivity
// for of 遍历数组会自动跳过空元素
Array.prototype.flat4 = function (deep = 1) {
    const result = [];
    (function flat(arr, deep) {
        for(let item of arr) {
            if (Array.isArray(item) && deep > 0) {
                flat(item, deep - 1);
            } else {
                // 去除空元素,因为void 表达式返回的都是undefined,不适用undefined是因为undefined在局部变量会被重写
                item !== void 0 && result.push(item);
            }
        }
    })(this, deep);

    return result;
}
20.3.5 使用堆栈stack

该方式主要利用堆栈的思想,将一个多层数组全部展开为一层。其思想可总结为以下几个步骤:

  1. 将要处理的数组放到一个栈中处理;

  2. 从栈顶取出元素,判断该元素类型,若为数组,则将该数组展开再放回栈顶;若为普通元素则将其放到结果中;

  3. 循环遍历,至到栈为空。

// 使用堆栈stack
Array.prototype.flat5 = function() {
    const stack = [...this];
    const result = [];
    while (stack.length > 0) {
        const next = stack.pop();
        if (Array.isArray(next)) {
            stack.push(...next);
        } else {
            result.push(next);
        }
    }

    // 反转恢复原来顺序
    return result.reverse();
}

1.如果觉得这篇文章还不错,来个分享、点赞、在看三连吧,让更多的人也看到~

2.关注公众号执鸢者,领取学习资料,定期为你推送原创深度好文

3.关注公众号进群,里面大佬多多,一起向他们学习

1. 前端百题斩[001]——typeof和instanceof

2. 前端百题斩【002】——js中6种变量声明方式

3. 前端百题斩【003-004】——从基本类型、引用类型到包装对象

4. 前端百题斩【005】—— js中9种遍历对象的方法

5. 前端百题斩【006】——js中三类字符串转数字的方式

6. 前端百题斩【007】——js中必须知道的四种数据类型判断方法

7. 前端百题斩【008-009】——从JavaScript的代码执行过程到函数执行过程

8. 前端百题斩【010】——通俗易懂的JavaScript执行上下文

9. 前端百题斩【011】——通俗易懂的变量对象

10. 前端百题斩【012】——js中作用域及作用域链的真面目

11. 前端百题斩【013】——用“闭包”问题征服面试官

12. 前端百题斩【014】——js中的这些“this”指向都值得了解

13. 前端百题斩【015】——快速手撕call、apply、bind

14. 前端百题斩【016】——原型、构造函数和实例之间的奇妙关系

15. 前端百题斩【017】——一基础、二主线、双机制理解原型链

16. 前端百题斩【018】——从验证点到手撕new操作符

17. 前端百题斩【019】——数组中方法原理早知道

18. 前端的葵花宝典——架构

19. canvas从入门到猪头

20. 前端工程师的一大神器——puppeteer

21. 2021 年前端宝典【超三百篇】

22. 前端也要懂机器学习(上)

23. 前端也要懂机器学习(下)

24. 学架构助力前端起飞

25. 假如只剩下canvas标签

26. Vue源码思想在工作中的应用

27. 一文搞定Diff算法

28. 百度、小红书三面,均遇“赛马”问题

29. 十五张图带你彻底搞懂从URL到页面展示发生的故事

30. 一文搞懂Cookie、Storage、IndexedDB

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值