Array.apply生成陣列小結

我不是你煌哥哥發表於2019-04-13

發現問題

今天在學習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]
複製程式碼

相關文章