手记

分析Array.apply(null, { length: 20 })

一、背景

在阅读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文档作者的基础功力。

  1. 如果为了少写几个字的话还可以把该表达式修改成:
    Array.apply(null, Array(20)); // 第二个参数用Array(20)代替{length: 20}
  2. 还可以使用ES6 API更直观表达意图:

    // 方法1:
    Array.from({length: 20})
    
    // 方法2
    Array(20).fill(null)

    四、参考

  3. Apply函数
  4. Array.prototype.map方法
0人推荐
随时随地看视频
慕课网APP