测试一个问题时,希望创建一个数组变量,长度为100,每个元素值为‘a’。第一反应就是先创建一个length为100的数组,再用map方法把每个元素转为’a’,写法如下:
let arr = Array(100).map(item=>item='a')
结果瞬间打脸,得到的数组是[empty × 100],根本不是需要的。
为啥会这样呢,查map在mdn中的定义,发现了一段关键的话:
callback is invoked only for indexes of the array which have assigned
values (including undefined). It is not called for missing elements of
the array; that is:
- indexes that have never been set;
- indexes which have been deleted.
也就是说,只有指定了值的元素才会触发map的回调函数,没有初始化或者索引被删除的元素是无法被遍历的。
回到开头的问题,Array(100)生成的数组其中的元素没有被初始化,所以也就没有触发回调函数,数值没有变更。
那么我们的问题可以拆解一下:
- 生成长度为100的数组
- 初始化元素
- map回调函数改变元素值
思路一:
先将数组转为字符,再转回数组类型
let arr = Array(100).toString().split(',').map(item=>item='a')
思路二
还可以巧妙借助apply方法
let arr = Array.apply(null,{length:100}).map(item=>item='a')
apply的第一个参数为null时,会将apply前面的fun函数指向全局函数,第二个参数可以为数组或类数组,类数组会转成数组。
Array.apply(null,{length:100})相当于Array.apply(null,[undefined,undefined,…]),数组已被初始化,元素值为undefined。
思路三
受前面类数组转为数组的启发,使用Array.from来处理。
let arr = Array.from({length:100},(v,k)=>'a')
Array.from是ES6新增的方法,可以将类数组和可迭代对象转为数组,并且自身就包含类似map的处理方法。
// 语法结构
Array.from(arrayLike[, mapFn[, thisArg]])
那么,[].slice.call()同样可以将类数组转为数组,是不是可以用[].slice.call()来进行类似的操作呢?
答案是否定的。
let _arr = [].slice.call({length:100})
console.log(_arr)
// [empty x 100]
但是这样得到的数组元素没有被初始化。这个从V8源码中可以看出端倪。
Array.from部分源码:
slice部分源码:
slice会先判断数组中是否包含某个索引,没有就跳过,而Array.from会直接对数组赋值,没有初始值就会赋值为undefined。所以在这里,Array.from(100)得到的是[undefined,…],slice得到的是[empty × 100],slice是没办法直接满足我们的要求的。
类数组对象:包含length属性,且length属性值为整数的对象。
可迭代对象:具有原生iterator接口的数据结构,包括Array、Map、Set、String、TypedArray、函数的 arguments 对象、NodeList 对象。
总结
最后,生成指定长度指定元素值的数组,方法如下:
- new Array
let arr = Array(100).toString().split(',').map(item=>item='a')
- Array.apply
let arr = Array.apply(null,{length:100}).map(item=>item='a')
- Array.from
let arr = Array.from({length:100},(v,k)=>'a')
- for循环
var arr1 = new Array(100);
for(var i=0;i<arr1.length;i++){
arr1[i] = i;
}
- push
var arr = new Array();
for(var i=0;i<100;i++){
arr.push(i);
}