Array 可能跟你想的不一樣(下)

osk2
5 min readNov 4, 2019
Photo by Bharat Patil on Unsplash

怕你還沒看過上集還是先來個前情提要

上集解釋了 new Array() 與 [] 之間的差異,還有使用 new Array() 時要注意的地方

const ary = [undefined, undefined, undefined];ary.forEach(v => {
console.log(v);
});
// 印出三次 undefined
const ary2 = new Array(3);
ary3.forEach(v => {
console.log(v);
});
// 這裡卻什麼都沒印出來

這集就來好好解釋一下 forEach() 是如何運作的,為何上面這段程式沒有如我們想像的執行

Array.prototype.forEach() 如何運作?

要回答這個問題最好的方式就是直接看 ECMAScript 是如何實作 forEach() 的

首先我們到 ECMAScript 查查定義

裡頭提到了 forEach() 的每個步驟

ECMAScript 對 Array.prototype.forEach() 的步驟詳解

我概略列出裡頭跟我們有關的重點:

  1. 先找出陣列的長度(length 屬性)
  2. 開始 while 迴圈
  3. 並檢查陣列是否有名為目前索引值的屬性
  4. 如果上面的條件成立就呼叫 callback(否則什麼事都不做)
  5. 迴圈執行完畢返回 undefined

重點在第三行,還記得上集我們介紹過,用 new Array(3) 建立的陣列只有 length 屬性嗎?

假設目前迴圈第一次執行,接著會查詢 ary[0] 是否存在,結果會是 undefined,也就不會呼叫 callback,這也是為什麼迴圈看起來沒有執行的原因(實際上還是有,只是從來沒有呼叫 callback)

謎題終於解開啦,只是陣列 prototype 中的方法都是類似的處理方式嗎?

同場加映

在上集結尾提到了 fill() 和 join()

new Array(10).join(' ');new Array(3).fill('5566');

你可能又會好奇:難道只有 length 的陣列在這裡又有用途了嗎?

沒錯,先來看看 Array.prototype.join() 的解釋:

ECMAScript 對 Array.prototype.join() 的步驟詳解

一開始會建立一個空字串 R,並在迴圈中依序將每個值與分隔符號(Seperator)附加到 R,最後回傳這個字串

在我們的例子中,陣列中每個值都是 undefined,在 7.c 會被轉為空值,因此在迴圈中每次就只會附加分隔符號到 R,也就是空格,所以最終我們會得到一個全是空格的字串

至於為何 new Array(10) 只產生九個空格呢?好好把步驟 7 再看一次吧!

而 Array.prototype.fill() 也是類似步驟,詳情可以自行參閱 ECMAScript

小技巧

前面提到在 forEach() 執行步驟中,只要有 length 和相對應的屬性就能順利執行

這時聰明的你就會想到:那我能自己建立一個類陣列(Array-like)的物件,然後借用一下 forEach() 強大的功能嗎?

當然可以!

const fakeArray = {  "0": 1,  // 記得陣列索引從 0 開始
"1": 2,
"length": 2,
};Array.prototype.forEach.call(fakeArray, console.log);
/*
執行結果
1 0 {0: 1, 1: 2, length: 2}
2 1 {0: 1, 1: 2, length: 2}
*/

這集大概4醬,如果有哪裡解釋得不好,甚至是錯誤,請一定要告訴我 🙏

然後雖然不知道拍手可以幹嘛,不過喜歡的話還是滿足一下我的虛榮吧 👏👏

References

--

--