JavaScript 特殊物件 Array-Like Objects 詳解

韓子遲發表於2016-06-26

這篇文章拖了有兩週,今天來跟大家聊聊 JavaScript 中一類特殊的物件 -> Array-Like Objects。

(本文節選自 underscore 原始碼解讀系列文章,完整版請關注 https://github.com/hanzichi/underscore-analysis

Array-Like

JavaScript 中一切皆為物件,那麼什麼是 Array-Like Objects?顧名思義,就是像陣列的物件,當然,陣列本身就是物件嘛!稍微有點基礎的同學,一定知道 arguments 就是 Array-Like Objects 的一種,能像陣列一樣用 [] 去訪問 arguments 的元素,有 length 屬性,但是卻不能用一些陣列的方法,如 push,pop,等等。

那麼,什麼樣的元素是 Array-Like Objects?我們來看看 underscore 中對其的定義。

很簡單,不是陣列,但是有 length 屬性,且屬性值為非負 Number 型別即可。至於 length 屬性的值,underscore 給出了一個上限值 MAX_ARRAY_INDEX,其實是 MAX_SAFE_INTEGER(感謝 @HangYang 同學指出) ,因為這是 JavaScript 中能精確表示的最大數字。

想想還有什麼同時能滿足以上條件的?NodeList,HTML Collections,仔細想想,甚至還有字串,或者擁有 length 屬性的物件,函式(length 屬性值為形引數量),等等。

Array-Like to Array

有的時候,需要將 Array-Like Objects 轉為 Array 型別,使之能用陣列的一些方法,一個非常簡單粗暴並且相容性良好的方法是新建個陣列,然後迴圈存入資料。

我們以 arguments 為例。

但是這不是最優雅的,更優雅的解法大家一定都知道了,use Array.prototype.slice(IE9- 會有問題)。

或者可以用 [] 代替 Array.prototype 節省幾個位元組。

如果非得追求效能,用 [] 會新建個陣列,效能肯定不及前者,但是由於引擎的優化,這點差異基本可以忽略不計了(所以很多框架用的就是後者)。

為什麼這樣可以轉換?我們簡單瞭解下,主要的原因是 slice 方法只需要引數有 length 屬性即可。首先,slice 方法得到的結果是一個 新的陣列,通過 Array.prototype.slice.call 傳入的引數(假設為 a),如果沒有 length 屬性,或者 length 屬性值不是 Number 型別,或者為負,那麼直接返回一個空陣列,否則返回 a[0]-a[length-1] 組成的陣列。(具體可以看下 v8 原始碼 https://github.com/v8/v8/blob/master/src/js/array.js#L621-L660

當然,ES6 提供了更簡便的方法。

小結下,如果要把 Array-Like Objects 轉為 Array,首選 Array.prototype.slice,但是由於 IE 下 Array.prototype.slice.call(nodes) 會丟擲錯誤(because a DOM NodeList is not a JavaScript object),所以相容的寫法如下。(但還有一點要注意的是,如果是 arguments 轉為 Array,最好別用 Array.prototype.slice,V8 下會很慢,具體可以看下 避免修改和傳遞 arguments 給其他方法 — 影響優化

Others

很多時候,某個方法你以為接收的引數是陣列,其實類陣列也是可以的。

Function.prototype.apply() 函式接收的第二個引數,其實也可以是類陣列。

Read More

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

JavaScript 特殊物件 Array-Like Objects 詳解

相關文章