從如何優雅的將類數組對象轉化爲數組談起

今日研讀阮老師的ES6標準入門,讀到函數的擴展方法時看到這麼一段代碼:

let arrayList = {
    "0":"a",
    "1":"b",
    "2":"c",
    "length":3
}
var arr = [].slice.call(arrayList) // ["a","b","c"]

很明顯,上面代碼轉化代碼的關鍵就是[].slice.call()這一句了。
鄙人不才,平常在項目裏碰到需要解構的json數據時都是直接懟上for/forin硬肛的,現在想想真是羞愧啊,往事不提不提~
阮老師的這個demo勾起了我的興趣,那麼我就從call()講起。
要理解var arr = [].slice.call(arrayList) // ["a","b","c"]這句代碼的意思,你需要有以下三點需要理解:

  1. [ ]是什麼?
  2. slice()函數起到什麼作用?
  3. call()函數起到什麼作用?

先講第一點。[ ]是什麼,J開頭的程序員肯定都知道,js裏的數組嘛,沒什麼好說的。值得講講的就一點,就是[]與new Array()到底有什麼區別?它們到底哪個效率高?[]到底需不需要分配內存?我不做回答,大家可以自行探究。

再講第二點,slice()函數,我們來看官方MDN定義:

slice() 方法返回一個從開始到結束(不包括結束)選擇的數組的一部分淺拷貝到一個新數組對象。且原始數組不會被修改.

這就很明顯了,這就是[]對象(js裏函數也是一個對象)調用了一下slice()函數嘛,爲啥調用它,因爲它返回新的數組對象呀。那麼問題又來了,是不是只要會返回新的數組對象的函數都可以在這裏調用呢?我們暫且先不談論,先往下講。

最後說說第三點,call( )函數。我們來看MDN定義:

Function.prototype.call()
call() 方法調用一個函數, 其具有一個指定的this值和分別地提供的參數(參數的列表)。

當然,MDN對此函數還有補充:

語法

fun.call(thisArg, arg1, arg2, …)

參數

thisArg
在fun函數運行時指定的this值。需要注意的是,指定的this值並不一定是該函數執行時真正的this值,如果這個函數處於非嚴格模式下,則指定爲null和undefined的this值會自動指向全局對象(瀏覽器中就是window對象),同時值爲原始值(數字,字符串,布爾值)的this會指向該原始值的自動包裝對象。

返回值

返回值是你調用的方法的返回值,若該方法沒有返回值,則返回undefined。

簡單來說呢,就是我們調用call()函數的根本目的,就是爲了去解決js裏單個object對象沒有length屬性,也調用不了slice()函數的痛點,當然這個調用也是通過prototype.call()這個原型鏈去調用。
如果你嘗試用這句代碼

var arr = [].slice.call(arrayList,1)  //["b","c"]

call(this,args1,args2…)函數中this之後的參數會隨之傳遞給slice()函數。

如果我們把call()函數換成apply()可以得到一樣的結果嗎?答案是肯定的。

let arrayList = {
    "0":"a",
    "1":"b",
    "2":"c",
    "length":3
}
var arr = [].slice.apply(arrayList) // ["a","b","c"]

但是如果我們把call()換成bind()呢?還可以得到數組嗎?答案是否定的。

let arrayList = {
    "0":"a",
    "1":"b",
    "2":"c",
    "length":3
}
var arr = [].slice.bind(arrayList) // function slice(){...]

經測試,bind()函數果然不負衆望,給咱們返回了一個slice()函數。究其原因,是因爲:

bind 是返回對應函數,便於稍後調用;apply 、call 則是立即調用 。

那麼,除了以上幾個方法外還有什麼可以‘優雅’的轉化數組的方式嗎?答案是毋庸置疑的。
首先掛上ES6裏已經實現的函數Array.from()
MDN:

Array.from() 方法從一個類似數組或可迭代對象中創建一個新的數組實例。

使用起來也異常暴力,深諳簡單就是藝術的美學:

let arrayList = {
    "0":"a",
    "1":"b",
    "2":"c",
    "length":3
}
var arr = Array.from(arrayList) // ["a","b","c"]

至於兼容性情況,對於萬惡之源IE瀏覽器來說肯定是不支持的,這輩子都不可能支持的。(不接受拿Edge來跟我擡槓)

我們還有一個Object.values()函數可以達到類似的目的。下面關門放MDN:

Object.values()方法返回一個給定對象自己的所有可枚舉屬性值的數組,值的順序與使用for…in循環的順序相同 ( 區別在於for-in 循環枚舉原型鏈中的屬性 )。

關於什麼是可枚舉性,這個就不深說,簡單來說就是用for-in迭代時能取到對象裏所有的屬性。
我們來看代碼:

let arrayList = {
    "0":"a",
    "1":"b",
    "2":"c",
    "length":3
}
var arr = Object.values(arrayList) // ["a","b","c"3]

看到區別了嗎?這次返回的數組多了一個5,很顯然它是把對象裏的所有key對應的value返回出來了。再看幾個例子:

let arrayList = {
    "a":"1",
    "b":"2",
    "c":"3",
}
var arr = Object.values(arrayList) // ["a","b","c"3]

let arrayList1 = {
    "a":"aa",
    "b":"bb",
    "c":"cc",
}
var arr1 = Object.values(arrayList1) // ["aa","bb","cc"]

以上的例子充分說明Object.values()對引用的參數對象是沒有類數組對象那種格式限制的。

當然,兼容性方面除了IE外,其他瀏覽器稍微高一點版本都是支持的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章