Array.apply生成数组小结

2,936 阅读4分钟

发现问题

今天在学习vue时,在文档的一个例子中发现一个代码片段

  Array.apply(null, { length: 20 }).map(function () {
    return createElement('p', 'hi')
  })
  // 目的是为了创建一个20次的循环

这让我想起来之前在学习中写demo时,想要用forEach来做一个若干次的循环(觉得写for循环麻烦),当时是这样写的

  Array(10).forEach(function(){
    // do something
  })

结果是并不会进入循环,后来查询mdn原因如下:

forEach 方法按升序为数组中含有效值的每一项执行一次callback 函数,那些已删除(使用delete方法等情况)或者未初始化的项将>>被跳过(但不包括那些值为 undefined 的项)(例如在稀疏数组上)。

划重点: 未初始化的项将被跳过

原来因为通过 Array(10) 或者 new Array(10) 方式创建的数组是一个有length属性的空数组,其中的每个元素还没有被赋值(初始化),所以会变forEach map 等方法跳过

  Array(10) // [empty × 10] length: 10
  new Array(10) // [empty × 10] length: 10

解决问题

但是通过今天看到的这个方法就可以创建一个可以被forEach等方法遍历的数组,这是为什么呢?

  // 这三个方法返回的是一个长度为10的数组,且每一个元素都被赋值成undefined
  Array.apply(null, {length: 10}) 
  Array.apply(null, Array(10)) 
  Array.apply(null, new Array(10)) 

  // [undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined]

这里先复习一下Array构造函数的用法,下面是mdn的引用

Array 构造器会根据给定的元素创建一个 JavaScript 数组, 但是当仅有一个参数且为数字时除外: 一个范围在 0 到 232-1 之间的整数, 此时将返回一个 length 的值等于 arrayLength 的数组对象(言外之意就是该数组此时并没有包含任何实际的元素, 不能理所当然地认为它包含 arrayLength 个值为 undefined 的元素)。 注意,后面这种情况仅适用于用 Array 构造器创建数组,而不适用于用方括号创建的数组字面量。

划重点 但是当仅有一个参数且为数字时,组此时并没有包含任何实际的元素,不能理所当然地认为它包含 arrayLength 个值为 undefined 的元素 就是说创建出来的数组中的元素并没有被赋值(初始化)

然后再复习一下Array.apply方法的使用

Array.apply() 方法接收两个参数, 第一个为调用时指定的上下文(context); 第二个为一个数组或者一个类数组对象;

  Array.apply(null, {length: 10})
  Array.apply(null, Array(10))
  Array.apply(null, new Array(10))

  // 这三个方法相当于

  Array(undefined, undefined, undefined, ...)

因为Array构造函数会根据给定的arguments来创建一个数组(如果不是仅有一个参数且为数字的话), 通过Array.apply传入一个length为10的空数组则相当于, 把一个空数组中的每一个元素的值逐个传入Array()方法, 而空数组中的每一个元素 比如: emptyArr[0] 的值都是 undefined

为了便于理解这里写一个伪代码; 伪代码:

  var arrayLike = {length: 2}
          ↓↓
  Array.apply(null, arrayLike)
          ↓↓
  Array(arrayLike[0], arrayLike[1]) // 把一个空数组中的每一个元素的值逐个传入Array()方法
          ↓↓
  Array(undefined, undefined) // 而空数组中的每一个元素的值都为undefined

  //最终输出 [undefined, undefined]

这也就是为什么可以通过Array.apply()方法可以生成一个可供forEach等方法遍历的数组

  Array.apply(null, {length: 5}) 
  // 上面这样写实际上等于下面这样写
  Array(undefined, undefined, undefined, undefined, undefined)

  // [undefined, undefined, undefined, undefined, undefined]
  // 生成的数组里的元素都被初始化成了undefined,就可以被forEach等方法遍历了

另外es6提供了一个新的api,Array.from()也可以做到相同的结果

以下是阮一峰老师的《ECMAScript 6 入门》的引用

Array.from方法还支持类似数组的对象。所谓类似数组的对象,本质特征只有一点,即必须有length属性。因此,任何有length属性的>对象,都可以通过Array.from方法转为数组

  Array.from({length: 5})
  // [undefined, undefined, undefined, undefined, undefined]

es6的另一种写法

  Array(...Array(5))
  // 或者
  [...Array(10)]
  // [undefined, undefined, undefined, undefined, undefined]