稀疏数组的理解

数组可以包含“空槽”,这与用值undefined填充的槽不一样。空槽可以用以下方式创建:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Array构造函数
const a = Array(5) // [empty × 5]

// 数组字面量中的连续逗号:
const b = [1,2,,,5] // [1,2,empty × 2, 5]

// 直接给大于数组length的索引设置以形成空槽:
let c = [1,2]
c[4] = 5 // [1,2,empty × 2, 5]

// 通过设置length长度拉长一个数组:
const d = [1,2]
d.length = 5 // [1, 2, empty × 3]

// 通过delete删除一个元素:
const e = [1, 2, 3, 4, 5];
delete e[2]; // [ 1, 2, <1 empty item>, 4, 5 ]

在某些操作中,空槽的行为就像它们被填入了undefined一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const arr = [1, 2, , , 5]; // 创建一个稀疏数组
// 通过索引访问:
arr[2] // undefined


// for...of
for (const i of arr) {
console.log(i);
}
// 1
// 2
// undefined
// undefined
// 5

// 展开运算
const another = [...arr] // [1,2,undefined, undefined, 5]

在其他方法中,特别是数组迭代中,空槽是被跳过的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
const arr = [1,2,,,5] // [1, 2, empty, empty, 5]
const another = [...arr] // [1, 2, undefined, undefined, 5]

// map
arr.map((i) => i + 1) // [2, 3, empty × 2, 6]
another.map((i) => i + 1) // [2, 3, NaN, NaN, 6]

// forEach
arr.forEach((i) => console.log(i)) // 1, 2, 5
another.forEach((i) => console.log(i)) // 1, 2, undefined, undefined, 5

// filter
arr.filter(() => true)
// (3) [1, 2, 5]
another.filter(() => true)
// (5) [1, 2, undefined, undefined, 5]

// some
arr.some((k) => !k);
// false
another.some((k) => !k);
// true

// 属性迭代
Object.keys(arr)
// (3) ['0', '1', '4']
Object.keys(another)
// (5) ['0', '1', '2', '3', '4']

Object.values(arr)
// (3) [1, 2, 5]
Object.values(another)
// (5) [1, 2, undefined, undefined, 5]

for (const key in arr) {
console.log(key);
}
// (3) ['0', '1', '4']
for (const key in another) {
console.log(key);
}
// (5) ['0', '1', '2', '3', '4']

// 在对象中展开,使用属性枚举,而不是数组迭代器
const target = {...arr}
// {0: 1, 1: 2, 4: 5}
const target1 = {...another}
// {0: 1, 1: 2, 2: undefined, 3: undefined, 4: 5}

稀疏数组中的空槽在数组方法之间的行为不一致。通常,旧方法会跳过空槽,而新方法将它们视为 undefined。