在讨论数组初始化之前,首先要知道数组的两种创建方式:
以上两种数组创建的方式都可能产生数组空位,例如:
const s1 = new Array(10)
上述虽然创建了一个长度为10的数组,但是在控制台打印的结果为:
(10) [empty × 10]
这里的empty就是数组中的空位,通常含有数组空位的数组称为稀疏数组,相反的,没有数组空位的数组称为密集数组。那么数组空位会造成什么影响呢?
const s2 = s1.map(() => 1)
上述代码中,我们想通过map方法对s1进行遍历返回一个值全为1的数组,但是最终s2打印出来仍然是一个含有10个数组空位的数组。
这里可以查看V8中关于map方法的实现:
function ArrayMap(f, receiver) {
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.map");
// Pull out the length so that modifications to the length in the
// loop will not affect the looping and side effects are visible.
var array = TO_OBJECT(this); // (1)转化为对象
var length = TO_LENGTH(array.length); // (2)获取长度用于for循环
if (!IS_CALLABLE(f)) throw %make_type_error(kCalledNonCallable, f);
var result = ArraySpeciesCreate(array, length);
for (var i = 0; i < length; i++) {
if (i in array) { // (3)判断该下标是否存在数组对象中
var element = array[i];
%CreateDataProperty(result, i, %_Call(f, receiver, element, i, array));
}
}
return result;
}
结合上述源码可以发现数组空位本质上就是没有向数组对象中加入该下标的属性名,从而导致无法执行该下标的操作。
下面是数组空位对数组方法的影响:
最好的方法是在数组的创建过程中,减少产生数组空位的情况,但是有时候无法避免数组空位,例如采用Array构造函数传入数组长度的方式创建数组,就需要结合上述总结的情况,从而避免数组空位的影响。
以初始化一个长度为10并且元素值为下标值的数组为例。
最简单的方法就是采用for循环:
const demo1 = []
for (let i = 0; i < 10; i++) {
demo1[i] = i
}
对于该for循环还可以采用花哨一点的写法:
const demo2 = []
for (let i = 0; i < 10; demo2[i++] = i - 1);
在前面一节中,我们知道ES5中函数式的遍历方法都会忽略数组空位的处理,那么就需要通过ES6方法将数组空位转化掉,再采用ES5中函数式的遍历方法赋值。
第一种,Array.from() + map()
Array.from(new Array(10)).map((item, index) => index)
第二种,fill() + map()
new Array(10).fill(0).map((item, index) => index)
第三种,ES6扩展运算符(…) + map()
[...new Array(10)].map((item, index) => index)
除了上述两大处理方法之外,还有一种是结合类数组转化为数组的奇技淫巧。
Array.from({ length: 10 }).map((item, index) => index)
在ES5中可以采用apply方法进行同样的处理:
Array.apply(this, { length: 10 }).map((item, index) => index)
数组空位在ES5和ES6中表现的差异性很大,从上述数组初始化的操作中,可以发现当数组空位发生时,需要先处理数组空位,才能避免数组空位对于ES5中一些函数式的遍历方法所造成的影响。
----------------------------
----------------------------
您还可以在这些地方找到我: