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