一、背景
在阅读VueJS教程时碰到这段Demo code:
render: function (createElement) {
return createElement('div',
Array.apply(null, { length: 20 }).map(function () {
return createElement('p', 'hi')
})
)
}
其中这个表达式Array.apply(null, { length: 20 })
有点让人费解。第一感觉这个表达式就是为了创建一个长度为20的数组,但表达式Array(20)
也可以实现这个功能啊,为啥非要写那么复杂呢?看来情况没那么简单。。。
二、Array.apply(null, { length: 2 })
的值
分析Array.apply(null, { length: 2 })
的值之前我们先温故下基础(为了方便验证将表达式改成Array.apply(null, { length: 2 }),即length的值改成2):
基础1: Array构造函数
直接调用Array函数跟new方式调用是等价的,即:
var a = Array(2); // 等价于var a = new Array(2);
表示:创建一个长度为2的数组,注意该数组的元素并没有被初始化,即:
console.log(0 in a); // false
console.log(1 in a); // false, 因为数组下标0,1还未初始化
console.log(a[0]); // undefined, 因为数组下标0还未初始化,访问不存在的属性返回undefined
基础2: apply函数
ES5开始apply
函数的第二个参数除了可以是数组外,还可以是类数组对象(即包含length属性,且length属性值是个数字的对象)。对象{length: 2}
就是一个类数组对象,因为没有初始化下标0,1的值,所以获取0,1下标的值得到的都是undefined。
console.log(a[0]); // undefined
console.log(a[1]); // undefined
// 可以转成真正的数组
var a = Array.prototype.slice.call({length: 2});
console.log(Array.isArray(a)) // true
再看表达式Array.apply(null, { length: 2})的值
温故了基础后再看表达式Array.apply(null, { length: 2 })
他就等价于:
// 1 熟悉一点: {length: 2}作为Array.apply第二个参数等同于[undefined, undefined]作为Array.apply第二个参数
Array.apply(null, [undefined, undefined]);
// 2 再熟悉一点:apply方法的执行结果
Array(undefined, undefined);
// 3 再再熟悉一点:Array方法直接调用和new方式调用等价
new Array(undefined, undefined);
这样就很容易知道该表达式的值是一个长度为2,且每个元素值都被初赋值为undefined的数组(注意此时不是数组元素没有初始化,而是初始化成undefined,这就是跟Array(2)的区别)。
三、为啥非要写那么复杂呢?
回到最初的问题:为啥非要写那么复杂呢?回答这个问题前还得温故下map方法(来自MDN描述):
It is not called for missing elements of the array (that is, indexes that have never been set, which have been deleted or which have never been assigned a value).
即map函数并不会遍历数组中没有初始化或者被delete的元素(有相同限制还有forEach, reduce方法)。OK,疑问到此终于真相大白了:写这么“复杂”就是为了实现:创建一个长度为20,且每个元素都被初始化的数组。这样map方法就可以循环20次了。
// 被初始化的数组
Array.apply(null, {length: 20}).map(function(val, index){
console.log(index); // 循环20次
});
// 未被初始化的数组
Array(20).map(function(val, index){
console.log(index); // 不会被执行
});
其实这已经是实现该功能很简洁的写法了,不得不佩服vuejs文档作者的基础功力。
- 如果为了少写几个字的话还可以把该表达式修改成:
Array.apply(null, Array(20)); // 第二个参数用Array(20)代替{length: 20}
-
还可以使用ES6 API更直观表达意图:
// 方法1: Array.from({length: 20}) // 方法2 Array(20).fill(null)
四、参考
- Apply函数
- Array.prototype.map方法