本文同步自 JSCON簡時空 - 技術博客,點擊閱讀
視頻講解
文字講解
1、先講結論
有很多種方式將 arguments 轉換成數組,那麼哪一種方式是最優的?
爲節約大夥兒的時間,這裏先說一下結論:如果你想將 arguments 轉換成數組,最好的方式是使用 rest 參數轉換的方式(即使用 ...
spread 操作符),比如:
function test(…args) {
console.log(args)
}
test(1,2,3); // [1,2,3]
原因是:性能是 最優 的,可讀性也挺好。
想知道爲什麼的話,可以繼續往下看。
2、原因分析
arguments
對象是所有(非箭頭)函數中都可用的局部變量,它是一個 “Array-Like” 對象,即 “像數組的對象”的意思,有些文章中也會翻譯成 “僞數組對象”。(可以按索引取值、具有 length
屬性,但不一定具備 push
、concat
等數組方法,具體可參考文章僞數組(ArrayLike)內容)
!> 注意:箭頭函數中並不存在 arguments
對象
本期 tip 並不去詳細講 arguments 對象的知識內容(具體知識內容可閱讀本講末尾的參考文章),本講着重講解把它轉換成數組時的最佳實踐。
瀏覽了許多技術文章,將 arguments
對象轉換成數組基本是 4 種方式:
- 使用
Array.prototype.slice.call(arguments)
進行轉換,或者是使用等效方法[].slice.call(arguments)
; - 使用
Array.from(arguments)
進行轉換 - 使用
for
循環挨個將arguments
對象中的內容複製給新數組中 - 利用 ES6 中的 rest 參數轉換,
let a = (...args) => args
;
大多數文章也僅僅是講到這裏爲止,並沒有繼續討論以上哪種方式最優。
接下來我們就用基準測試(Benchmark)的方式來量化上述那種方式性能更好。
3、性能測試
在《做好準備:新的V8即將發佈,Node 的性能正在改變》文中給了結論:
我將這文中提及的測試代碼扔到 jsPerf
網站上(測試地址:https://jsperf.com/rest-arguments-slice ),運行結果如下:
圖中數值越高代表性能越好,以上兩幅圖所反映的結果是一致的:
- 利用 ES6 中的 rest 參數轉換性能最好
- 其次使用
for
循環方式轉換 [].slice
的方式性能較弱- 最差的就是用
Array.from
進行轉換
也可本地進行性能測試,測試代碼在 這兒 獲取;源碼來自 官方提供的 benchmark 示例
因此,如果你想要將 arguments
轉換成數組,那麼毫無疑問應當使用 ES6 中的 rest 參數轉換方式。
除了性能更好之外,rest
參數的用法相對於直接使用 arguments
還有如下優點:
- 箭頭函數和普通函數都可以使用。
- 更加靈活,接收參數的數量完全自定義。
- 可讀性更好,參數都是在函數括號中定義的,不會突然出現一個arguments,顯得很突兀。
4、Q & A
在這裏我簡單解答一些常見的疑惑:
Q: 爲什麼需要將 arguments
對象轉換成數組?
A: 答案也簡單,因爲 Array 實例提供了很多數組方法,比如 .push
、.concat
等,提供了更多數據操作方式,歸根到底,轉換成數組就是爲了方便操作數據。
Q: 既然經常要將 arguments
轉換成數組,爲什麼最初不把 arguments
設計成數組格式呢?
A: 按照文章 《JavaScript arguments 對象全面介紹》所言, arguments
在語言的早期就引入了,當時的 Array 對象具有 4 個方法: toString
、 join
、 reverse
和 sort
。arguments
繼承於 Object 的很大原因是不需要這四個方法。(當時設計的人也不知道後續的發展會對 arguments
有這方面的強需求...變化無處不在..)
Q: 爲什麼需要 Array-Like
對象(僞數組對象)的存在?
A: 前面說了,轉換成數組也是爲了提供更多數據操作方式;其實 Array-Like
對象的存在,也是爲了給數據提供更多的操作的可能,因爲可以在對象上掛載很多 自定義 的操作方法,使用起來靈活度會很高。
Q: 上述討論的數組轉換結果,是否也適應於其他 “僞數組對象”?
A: 因爲 arguments
也是“僞數組對象”,不難推而廣之,上面討論的數組轉換的方式都可以應用在“僞數組對象”上;至於每個轉換方法的性能如何,我因爲沒有單獨去測試過,所以也不能妄下定論,大家可以自己寫 benchmark 去測試一下(個人猜測應該結論也差不多)。
5、參考文章
- 做好準備:新的V8即將發佈,Node 的性能正在改變:官方譯文,V8 團隊新的 JIT 編譯器Turbofan 中很多以往的性能問題都獲得瞭解決,推薦閱讀;
- V8 性能優化殺手:這篇文章過時了,文中性能優化建議是針對 V8 上一代編譯器;之所以還陳列在這兒,是爲了方便和上一條參考文章做對比閱讀,加深對新 V8 引擎優化的瞭解。
- arguments is special:arguments 這個變量是特殊的,使用的時候需要多加註意
- JavaScript arguments 對象全面介紹:非常全面的介紹,介紹了它的來歷、注意事項
- JavaScript深入之類數組對象與arguments: 本文詳細講解了類數組和 arguments 對象
- 僞數組(ArrayLike):簡要介紹了類數組對象的概念和轉換;
- JavaScript: arguments leak var array.slice.call?: v8 無法優化 slice 方法的原因,是因爲該方法會保持對 arguments 對象的引用,無法將其優化成 stack 變量。
- Array-Like Objects and Generic Methods:犀牛書中對 “僞數組” 概念的解釋
- Arraylike的7種實現: 羅列了原生的僞數組列表,同時說明了僞數組出現的緣故 —— 它的出現爲一組數據的行爲(函數)擴展提供了基礎
- JavaScript類數組對象參考:JS 中有哪些僞數組對象?本文給了非常詳細的講解
- 如何創建僞數組:stackoverflow 上關於如何創建僞數組的討論
- JS Array From an Array-Like Object:本文羅列了將 Array-Like 對象轉換成數組的方法,基本就是和本 tip 中羅列的方式差不多
- ES6 系列之箭頭函數:本篇重點比較一下箭頭函數與普通函數,比較全面
- 詳解箭頭函數和普通函數的區別以及箭頭函數的注意事項、不適用場景:本文這篇文章中還講了很多 rest 參數的優點
關於 “前端Tips專欄”
“前端Tips”專欄,隸屬於是 JSCON 專欄系列,設計初衷是快速獲取前端小技巧知識,取材廣泛,涵蓋前端編程諸多領域。設計初衷是快速消費類知識,所以每個 tips 閱讀耗時大約 5 分鐘。爲方便讀者在不同場合閱讀,每篇 tips 配有視頻、音頻和文字,挑自己喜歡方便的就行。
有兩種方式獲取歷史 tips:
① 在公衆號內回 "tips" +"期號" 就可以。例如:回覆 “tips25” 即可獲取第25期 tips
② 前往網站:https://boycgit.github.io/fe-program-tips,裏面提供了搜索功能
歡迎大家關注我的知識專欄,更多內容等你來挖掘