数组是有序的列表
JavaScript 中的数组保存一个有序的值序列。这些值可以是任意类型:数字、字符串、对象,甚至是其他数组,而且不必都是同一种类型。用方括号就可以创建一个数组:
99% 的情况下,你都会用这种字面量语法创建数组。还有一种 new Array(...),但它有个坑:new Array(3) 创建的是一个长度为 3、没有任何实际元素的数组,而不是 [3]。坚持使用 [] 就好。
索引从零开始
你可以按位置访问元素,位置从 0 开始:
有两点需要注意。第一,索引超出数组末尾时不会报错,而是返回 undefined。这是很多“为什么这里是 undefined?”类 bug 的常见来源。第二,你可以给任意索引赋值,包括超出末尾的索引,JavaScript 会乐呵呵地把数组扩展开:
位置 3 和 4 变成了“空槽”(稀疏数组的空洞)。长度一下子跳到了 6。你很少会想要这种效果:它只是数组过于灵活带来的副作用,而不是一个值得依赖的特性。
length 是实时的
array.length 总是比最大索引大 1。添加或删除元素时,它会自动更新:
最后那个技巧值得记住:给 length 赋值会截断数组。设置 nums.length = 0 是就地清空数组的快捷方法。
push、pop、shift、unshift
四个用于在两端添加和删除元素的方法:
push(x)添加到末尾,返回新的长度。pop()从末尾删除,返回被删除的元素。unshift(x)添加到开头。shift()从开头删除。
push/pop 很快。shift/unshift 需要给其他所有元素重新编排索引,所以在大数组上更慢。日常大小的数组没问题,但如果你要处理数百万个元素,就值得留意了。
slice 与 splice
这两个方法名字相似,行为却完全不同。把它们分清楚,能省去你很多挠头的时间。
slice(start, end) 返回数组某一部分的副本。原数组不受影响。end 不包含在内:
splice(start, deleteCount, ...items) 会修改数组。它可以删除、插入,或两者都做,并返回它删除的内容:
经验法则:如果这个词让你想到从一条面包上切(slice)下一片副本,那就是 slice。如果它让你想到剪接(splice)胶片,也就是剪掉再插入,那就是 splice。
查找元素:indexOf、includes、find
三种查找方式,分别适用于不同的场景:
indexOf(x)返回位置,找不到时返回-1。在需要索引时很有用。includes(x)返回一个布尔值。如果你只想知道“它在不在里面?”,用它更简洁。它还能正确处理NaN,而indexOf不行。find(predicate)对每个元素执行一个函数,返回第一个匹配的元素。它适用于对象数组,因为indexOf和includes只做严格相等比较。
遍历数组
有几种好方法,各自适合不同的情况:
for...of 是默认选择:可读性好,适用于任何可迭代对象,还能直接拿到值。如果你还需要索引,并且不需要提前跳出循环(forEach 无法用 break 跳出),用 forEach 也可以。带索引的传统 for 循环写起来啰嗦,但能给你完全的控制权:需要跳过元素、倒序遍历,或两者都需要时就用它。
有一种循环要避免:for...in。它遍历的是键,而且会包含继承来的属性。它是给对象用的,不是给数组用的。
复制与合并
数组是引用值。把一个数组赋给另一个变量并不会复制它,只是给同一个数组起了两个名字:
要真正复制数组,用展开运算符或 slice():
两者得到的都是浅拷贝:顶层数组是新的,但如果元素是对象,这些对象仍然是共享的。展开运算符也是合并数组最简洁的方法:
对象数组
在实际代码中,大多数数组保存的都是对象。同样的方法依然适用,但匹配方式会更有意思:
filter 返回一个由符合谓词的元素组成的新数组。map 返回一个新数组,其中每个元素都经过了转换。把它们链式调用是处理对象数组的基本功,下一篇文档会更深入地介绍这一类方法。
数组与对象
一个简单的思维模型:当顺序重要,并且你处理的是一列同类事物时,就用数组。当你要按名字给东西设键时,就用对象(或 Map)。“第一个、第二个、第三个”是数组的地盘;“查找 id 为 42 的用户”是对象或 Map 的地盘。
你可以把它们混在一起用,对象数组随处可见。但不要把数组当作按键查找的字典,也不要把对象当作有序列表。它们各自擅长一件事。
下一页:解构
按索引从数组中取值(以及按名字从对象中取属性)是非常常见的操作,所以 JavaScript 为它提供了专门的语法。这就是解构,也是下一页的内容。
常见问题
JavaScript 中如何创建数组?
使用方括号字面量语法:const fruits = ['apple', 'banana']。这是最地道的写法。Array 构造函数(new Array(3))虽然存在,但很少是你想要的:new Array(3) 会创建一个长度为 3 却没有任何实际元素的数组,这常常让人意外。
JavaScript 中如何从数组中删除一个元素?
从末尾删除用 pop()。从开头删除用 shift()。从中间任意位置删除,用 splice(index, 1) 就地修改,或者用 filter(...) 得到一个不含该元素的新数组。splice 会改变原数组;filter 不会。
slice 和 splice 有什么区别?
slice(start, end) 返回数组某一部分的浅拷贝,不会动原数组。splice(start, deleteCount, ...items) 会修改原数组:它删除元素,可以选择插入新元素,并返回被删除的元素。只差一个字母,行为却大不相同。
如何检查一个值是否在 JavaScript 数组中?
用 array.includes(value):它返回 true 或 false,并且能正确处理 NaN。indexOf(value) 返回位置,找不到时返回 -1,在你同时需要索引时很有用。如果需要复杂的匹配,就用 some(predicate) 或 find(predicate)。