吐槽Javascript系列三:数组的陷阱

43次阅读

共计 2264 个字符,预计需要花费 6 分钟才能阅读完成。

虽然本系列是吐槽,但并不是为了黑 Javascript,而是揭露它的一些特性(怪癖),只有更好的了解它,才能更好的使用它。本篇主要介绍数组中常见的隐患点。
龟速的 map
在数组中,map 是一个功能很强大的方法,先来见识一下:
let arr = [5, 2, 0]
.map(v => {
return v * 2
})
.filter(v => {
return v > 5
})

console.log(arr) // [10]
它能返回一个新的数组,然后进行链式调用。别以为链式调用只有 ES6 中的 Promise 才有,es5 的数组中早有了!但据我观察,有些程序员会把它当成 forEach 来误用,如下:
let nums = [1, 3, 5, 7]
nums.map(v => {
console.log(v)
})
我问:你为什么要这样用呢?数组遍历应该用 forEach 和 for 循环来进行遍历,map 主要是用来做映射生成新数组。他答:map 也可以遍历啊,完全没有问题,并且 map 比 forEach 还少敲几个字母,不是更方便吗?我答:正常来说,的确可以这样用,但遇到大长度数组,涉及到性能的情况,要用 forEach 或 for,因为他们之间的性能有很大区别,我们来看一个例子
// map 1770ms 左右
let sum = 0
let arr = []
for (let i = 0; i < 10 * 1000 * 1000; i++) {
arr.push(i)
}

console.time(‘p’)
arr.map(v => {
sum += v
})
console.timeEnd(‘p’)
上面的例子中,如果用 map 来循环,在我的电脑上大约要 2s 的时间,而用 forEach,470ms 左右,用 for,则只需要 18ms 左右。对于前端而言还好,但如果是在 Node 中(服务端)呢,那可是致命的。一句话吐槽,map 很慢如龟速!
删除的陷阱
数组也是一个对象,可以用 delete 运算符来从数组中移除元素,如下:
let arr = [1, 3, 5, 7, 9]
delete arr[2]
console.log(arr)
但是这种方式,会导致数组中将留下一个空洞,对于上面的例子来说,数组中的第三项 5 被删除,数组长度依旧是 5,其他所有项的索引不变。有点占着茅坑不拉 shi 的感觉,常常不是我们想要的结果。所以删除常用 splice 方法来做,我们来看一个例子:
// 根据索引 curId,删除 list 中的项
let curId = 2
let list = [
{id: 1, name: ‘a’},
{id: 2, name: ‘b’},
{id: 3, name: ‘c’},
{id: 4, name: ‘d’}
]

list.forEach((v, index) => {
if (v.id === curId) {
list.splice(index, 1)
}
})
上面代码将删除 id 为 2 的对象,删除后,数组将只有 3 个元素。看上去没有什么问题。但如果数组 list 中有二个一样的项(且相邻)呢?如下:
let list = [
{id: 1, name: ‘a’},
{id: 2, name: ‘b’},
{id: 2, name: ‘b2’},
{id: 3, name: ‘c’},
{id: 4, name: ‘d’}
]
你会发现,name 为 b2 的项却删除不掉,这是为什么呢?因为 forEach 遍历删除第一项后,此时 index 为 2,而这时数组也实时改变了,这时的数组的第三项为 {id: 3, name: ‘c’},而{id: 2, name: ‘b2’} 则被跳过了,没有遍历到!这种情况,要用 for 循环来做,如下:
for (let i = 0; i < list.length; i++) {
if (list[i].id === curId) {
list.splice(i, 1)
i–
}
}
当删除一项,得将索引减 1,这样才能正确遍历每一项。总结一句话,删除看情况,请小心索引!
sort 的误用
小明是一个新手前端,他写了一个如下的升序排序:
const arr = [0, 1, 5, 10, 15, 10, 100, 99, 100]
arr.sort((v1, v2) => {
return v1 > v2
})
console.log(arr)
跑一跑,完全没有问题,看似很正确!但数据再多一点,如下:
const arr = [0, 1, 5, 10, 15, 10, -2, -2, 100, 99, 100]
就会发现结果已经不对了,排序不能这样写!正确的写法应该是这样:
arr.sort((v1, v2) => {
return v1 > v2 ? 1 : -1
})
上面二种写法看上去很像,但本质却很不一样,并且第一种写法在某些情况下返回的结果还是正确的,这正是隐患所在!总结一句话:数组排序,比较函数中请返回 1 /- 1 而不是 true/false!
unshift 命名之伤
每次看到这个方法,都会让我想起了 install 和 uninstall,STOP!它还有一个兄弟叫 shift,它们两兄弟一个用于往数组头部添加项,一个用于往数组头部删除项。请看例子:
let colors = new Array()
colors.unshift(‘black’)
console.log(colors) // [‘black’]

colors.unshift(‘red’, ‘green’)
console.log(colors) // [‘red’, ‘green’, ‘black’]

let item = colors.shift()
console.log(item) // red
console.log(colors) // [‘green’, ‘black’]
一句话总结,槽点就是命名太奇怪!
总结
虽然,上面提到的一些陷阱和槽点值得注意,但平心而论,js 中的数组是非常灵活的,其提供的很多方法用起来也很方便。

正文完
 0