前言
关于可遍历的指定长度的数组的定义:
- 可遍历:可以通过
map
、forEach
等方法来遍历数组元素。 - 指定长度:数组拥有指定数量的数组元素。
基础
创建一个指定长度的数组非常简单,直接在新建数组时初始化数组实例的长度。
new Array(5) /*等价于*/ Array().length = 5;
/**
(5) [empty × 5]
length: 5
__proto__: Array(0)
**/
复制代码
通过数组的构造函数创建数组实例并初始化长度,只是改变数组实例的 length
属性值,而数组所拥有的实际数组元素依然是空的 [empty × 5]
。
const i = 0;
Array(5).forEach((item,index)=>{console.log(i)}); //可以发现 i 并没有输出。
复制代码
※ 考虑到 JavaScript 中数组长度具有动态扩增的特性,因此我们没有必要对数组的长度进行初始化,因为很多判断程序都是基于数组
length
属性进行判定,这反而会导致一些意外的麻烦。
最简单粗暴的方式,就是创建数组时手动传入所有数组元素。
Array(0, 1, 2, 3, 4);
/**
(10) [1, 2, 3, 4, 5
0: 0
1: 1
2: 2
3: 3
4: 4
length: 5
**/
复制代码
但此种方式过于极端,所以我们还需要找到更通用的方式:
ES5,Apply方法与类数组
Array.apply(null, { length: 5 }).map(function() {
return 0;
});
复制代码
第一次看这种方式,还是在Vue的官方文档(不得不赞叹下!)
此种方式有两个要素:
apply
会将作为参数的数组扩展分解为一个一个普通参数进行传递。{length:5}
并不是一个普通对象,而是一个类数组 (Like Array)。
什么是类数组?
- 可以通过索引访问,并拥有
length
属性的对象- 不存在
push
、pop
等真实数组才具有的方法。
Like Array Type {length:10} Object NodeList [body.docs] {0: body.docs, length: 1} NodeList HTMLCollection [div.sidebar] {0: div.sidebar, length: 1} HTMLCollection
现在让我们执行下面的代码,并观察输出:
function applyTest() {
console.log(arguments);
}
applyTest.apply(null, { length: 5 });
applyTest.apply(null, [0, 1, 2, 3, 4]);
/**
> Object { 0: undefined, 1: undefined, 2: undefined, 3: undefined, 4: undefined }
> Object { 0: 0, 1: 1, 2: 2, 3: 3, 4: 4}
**/
复制代码
可以说这就是 Like Array
与 apply
方法相遇时产生的美丽巧合。也就是这份巧合,让我们可以替代手动并以自动的方式向数组的构造函数中一次传入多个参数。
一个题外话,因为类数组具有
length
属性并可通过索引进行遍历的特性,所以才可以用Array.prototype.slice.call({0:1,length:1}).
将其转换为真实的数组。
Array.prototype.slice.call(document.querySelectorAll('div'));
复制代码
ES6 的扩展运算符
[...Array(5)]; //[undefined, undefined, undefined, undefined, undefined]
复制代码
?? 如果用扩展运算符来转换一个Like Array
会如何?
[...{length:5}]; //VM146:1 Uncaught TypeError: {(intermediate value)} is not iterable
复制代码
执行后,发现直接报错了,这是因为ES6的扩展运算符在其内部实现上,做了一些基于不同情况的判断:
- 如果扩展的数组不是一个空数组,直接
[].concat(arr)
合并要扩展的数组。 - 如果扩展的数组是一个空数组,则基于该数组的
length
长度进行遍历,循环期间如果数组下标所对应的数组元素为空,则填充undefined
。 - 如果要扩展的对象不是一个数组,则判断该对象是否具有遍历器(Iterator)接口,否则抛错。
了解扩展运算符的基本工作原理后,现在我们就可以创建一个迭代器放入到创建的类数组中:
[...{ length: 5, [Symbol.iterator]: Array.prototype[Symbol.iterator] }];
复制代码
执行后,成功创建了拥有5个 undefined
元素的数组。
更简洁的 ES6 方式
Array(5).fill(0);
复制代码
最后
其实还有很多其它方式来实现创建可遍历的指定长度的数组,例如:
Array.from({ length: 5 }, () => 0);
Array.apply(null, Array(5)).map(() => 0);
复制代码
但个人感觉以上介绍到三种方式最具有代表典型。
除此之外,Vue 的 v-for="i in 10"
指令也可以支持按照指定的次数来渲染内容。