JS 基础! |清空数组性能最好的方式是...|const 也能修改?但能用这个吗?

1,479 阅读4分钟

「本文已参与低调务实优秀中国好青年前端社群的写作活动」

背景😯

今天重刷 leetcode 的时候,刷到这么一道题102. 二叉树的层序遍历,很简单的二叉树遍历题目,三下五除二把代码写了出来

其他代码都不重要,重要的是第 27 行那里。

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[][]}
 */
var levelOrder = function(root) {
    if(!root) return[]
    const res = [],
          que = [root, null]
    let tmpLevel = []
    while(que.length){
        const t = que.shift()
        if(t){
            tmpLevel.push(t.val)
            t.left && que.push(t.left)
            t.right && que.push(t.right)
        }else {// t为null
            res.push(tmpLevel)
            // tmpLevel.length = 0
            tmpLevel = []			//第27行
            que.length && que.push(null) 
        }
    }
    return res
};

const 和 let 的书写习惯

其他代码都不重要,重要的是第 27 行那里。我个人写代码的习惯时,能用const就用const,用了const后面要修改时报错了再改为let

不知道你们是怎么呢

所以自然我的tmpLevel一开始就是用const定义,也就自然而然地在tmpLevel = []处报错了。

改成let定义后,也是自然而然地过了,但是我一下想起来,好像用tmp.length = 0就能清空数组呀,还不用改const~ 说不定性能还高一点? 真机(han)智(han) [doge]

数组清空的方式😏

ok 我们先小小复习一下数组清空的方式

splice

首先是 JavaScript 数组自身API:splice

arr.splice 方法可以说是处理数组的瑞士军刀。它可以做所有事情:添加,删除和插入元素。

语法:arr.splice(start[, deleteCount, elem1,..., elemN])

它从索引 start 开始修改 arr:删除 deleteCount 个元素并在当前位置插入 elem1, ..., elemN。最后返回已被删除元素的数组。

那直接从下标为 0,删掉数组长度那么长不就好了~

let arr = [1,2,3]
arr.splice(0,arr.length);  
console.log(arr)//[]

arr.length

如果我们手动增加它,则不会发生任何有趣的事儿。但是如果我们减少它,数组就会被截断。该过程是不可逆的

let arr = [1, 2, 3, 4, 5];

arr.length = 2; // 截断到只剩 2 个元素
console.log(arr); // [1, 2]

arr.length = 5; // 又把 length 加回来
console.log(arr[3]); // undefined:被截断的那些数值并没有回来

也就是说,我们直接将其赋值为0 就行

let arr = [1, 2, 3, 4, 5];

arr.length = 0
console.log(arr); // []

= []

直接将 arr 重新赋值为空数组,之前的数组如果没有被引用就等待垃圾回收就完事了~

三种方式的性能比较😎

我当时就想啊,算法题,肯定要追求一下效率的吗,上性能最好的干他~

测试

let a = [];
console.time('splice');
for(let i =0 ; i < 10;i++){
    a = new Array(100).fill(111)
    a.splice(0,a.length); 
}
console.timeEnd('splice');

console.time('length');
for(let i =0 ; i < 10;i++){
    a = new Array(100).fill(111)
    a.length = 0;
}
console.timeEnd('length');

console.time('赋值[]');
for(let i =0 ; i < 10;i++){
    a = new Array(100).fill(111)
    a = [];
}
console.timeEnd('赋值[]')

一开始用了贼大的循环,直接卡死了🙃

结果

这里一开始看错小数点后位数了,感谢评论区指出~ ok 那就是直接赋值性能最好, 直接修改 length 为0 性能其次,但是相差不多呀,用lenngth还能保持我使用const的习惯,那就上 第二种方法呗

结果😐

结果一看,不对劲啊,这清零怎么把最后结果的也给清了,猛地一下想到原因,直呼自己睿智

原因

其实这个我是知道的,但是写题的时候真就没想起来

注意 JS 数组重置的方式,虽然经测试arr.length = 0的速度会比arr = []快很多,但是这样是得不到正确答案的,原因是因为:

arr =[]创建的是一个新的数组,并为其赋予一个新的引用。其他引用不收影响,仍指向原数组

arr.length = 0修改数组本身,就算通过不同的变量访问它,得到的是同一个数组


直观测试

我们做个测试直观一点

测试图解

也就是说 =[] 是创建了一个新数组,length=0之后操作的仍是原来的引用~所以答案数组中的自然也就给一同清零了

总结🤔

唉这个东西,真是基础不牢地洞山摇啊~虽然是非常基础的知识点,但是可能到实际运用中就可能带来一些没料到的 bug 。

一开始测到性能差异,先入为主的就以为因为=[]会创建一个新的引用吗~就会导致性能较差,导致性能测试结果都看歪来了😖也就导致自己"美滋滋地"用上不用改const的方法...

不过这也说明,该用 let时就用let啊,不要头铁...

基础、基础,还是 _ _的基础!