数组 | 数组空位和undefined

前情提要

举例1

之前在做算法题的时候遇到这样一个问题:我想创建一个n*m 的数组,于是我采用这样的方式

1
2
const n = 10, m = 10
const grid = Array(n).map(() => Array(m))

结果,grid打印的结果为[empty × 10],这说明map根本没起作用,阅读红宝书的时候发现在第四版141页数组空位的地方有说明,原话是“map()会跳过空位置”,用专业术语说,**map仅对每一项已分配值的索引调用**,所以要是我想实现我想要的效果,我应该写如下代码:

1
2
3
const n = 10, m = 10
const grid = Array(n).fill(0).map(() => Array(m))
// [Array(10), Array(10), Array(10), Array(10), Array(10), Array(10), Array(10), Array(10), Array(10), Array(10)] length = 10

举例2

我们已经直到,map方法会跳过空位置,并且红宝书第四版P140页提到”ES6中普遍将空位当作存在的元素,值为undefined“,下面的例子证实了这个说法

1
2
let arr = [1,2,,4];
console.log(arr[2] === undefined); // true

再看下面的代码:

1
2
3
4
5
6
7
8
9
let arr1 = [1,2,,4];
let arr2 = [1,2,undefined,4];

let arr1_n = arr1.map(item => {
return 2
}); // [2,2,,2]
let arr2_n = arr2.map(item => {
return 2
}); // [2,2,2,2]

咦?undefined的位置没有被跳过,而空值显然被跳过了

于是引发了两个问题:

  1. 尽管空值位置的值是undefined,但是和undefined还是有本质的区别,究竟是怎么样的区别呢?
  2. map是怎么判断是空值还是undefined的呢?通过arr[index] === undefined显然是行不通的

要解答这两个问题,就要回归到上面说的**map仅对每一项已分配值的索引调用**,undefined数据基本数据类型,当然属于值

空位和undefined

产生空位的操作

以下操作均会产生空位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Array构造函数传一个数值表示数组长度
let arr1 = Array(5); // [,,,,]

// 数组字面量创建
let arr2 = [,,,,,];

// length属性
let arr3 = [];
arr3.length = 5; // [,,,,,]

// 通过索引增加数组元素是超过数组长度
let arr4 = [1,2,3];
arr4[10] = 5; // [1,2,3,,,,,,,,,5]

// 删除数组元素产生空位
let arr5 = [1,2,3];
delete arr5[1]; // [1,,3]

用in操作符或者hasOwnProperty()检测空值和undefined

1
2
3
4
5
6
7
8
9
0 in [undefined, undefined, undefined];   // true
0 in [,,,] // false


let a = [1,2,,4];
let o = Object(a);

o.hasOwnProperty(2); // false
o.hasOwnProperty(1); // true

说明[undefined, undefined, undefined]在索引0处有值,[,,,]在索引0处没有值

不同方法对空值的处理

忽略空值

map():跳过空位,但会保留这个值

forEach(), filter(), every()some()reduce():跳过空位

join()toString()Array.from展开运算符:会将空位视为空字符串

fill():将空位视为正常值

copyWithin():连着空位一起拷贝

举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// map()
let arr = [1,,3];
let newArr = arr.map(item => item*2); // [2,,6];

// filter
let arr = [1,2,3,,,5,6];
arr.filter(item => item < 4); // [1,2,3];

// join
let arr = [1,,3];
arr.join('-') // 1--3

// fill
let arr = new Array(5).fill(0); // [0, 0, 0, 0, 0]

// copyWithin()
let arr = [1,2,3,4,,6,7];
arr.copyWithin(1, 3, 5); // [1,4,,4,,6,7]

// reduce()
let arr = [1,2,3,,4];
arr.reduce((prev, cur) => prev+cur); // 10

参考

JS中的数组空位和undefined