ECMAScript 2015 的 Array.from() 是很有趣的 Method,可將 Array-like Object 與 Iterable Object 轉成真正 Array,因此推導出很多有趣應用。
Version
macOS Mojave 10.14.5
VS Code 1.36.1
Quokka 1.0.236
ECMAScript 2015
Ramda 0.26.1
Array.from()
Array.from()
Array.from(arrayLike[, mapFn[, thisArg]])
將 array-like object 或 iterable 轉成 array
arrayLike:data 為 array-like object 或 iterable
mapFn:optional,可傳入 map function
thisArg:optional,可傳入取代 map function 中 this 的值
回傳值為 array。
有別於一般 array,Array.from() 接受兩種值:
- Array-like object
- Iterable
Array-Like Object
let data = {
0: 'Sam',
1: 'Kevin',
2: 'John',
length: 3
}
let objToArr = obj => {
let result = []
for(let i = 0; i < obj.length; i++) {
result.push(obj[i])
}
return result
}
objToArr(data) // ?
Array-like object 與一般 object 不同之處:
- Property 為 index:
0、1、2… - 有
lengthproperty
obj 可使用 for loop 與 [],看起來很像 array,所以稱為 array-like object。

let data = {
0: 'Sam',
1: 'Kevin',
2: 'John',
length: 3
}
let objToArr = obj => Array.from(obj).map(x => x)
objToArr(data) // ?
但因為 obj 只是 array-like object,並不是真正 array,所以沒有 Array.prototype.map() 可用。
但我們可透過 Array.from() 將 array-like object 轉成真正 array,就可使用 Array.prototype.map() 了。

let data = {
0: 'Sam',
1: 'Kevin',
2: 'John',
length: 3
}
let objToArr = obj => Array.from(obj, x => x)
objToArr(obj) // ?
但先透過 Array.from() 再使用 map() 有個缺點:中間會產生 intermediate array,因此影響效能。
若直接將 map function 傳給 Array.from() 的第二個 argument,結果完全相同,但不需 intermediate array,執行速度更快。

Iterable Object
String、Array、TypedArray、Map 與 Set 都稱為 iterable object,可使用 ES6 的 for...of loop。
String
let data = 'Sam'
let toArr = str => Array.from(str)
toArr(data) // ?
String 為典型 iterable object,因此可透過 Array.from() 轉成 char array。

Set
let data = new Set([1, 2, 3, 1])
let toArr = set => Array.from(set)
toArr(data) // ?
Set 亦為典型 iterable object,亦可使用 Array.from() 轉成 array。

Map
let data = new Map([[1, 2], [3, 4], [5, 6]])
let toArr = map => Array.from(map)
toArr(data) // ?
toArr(data.keys()) // ?
toArr(data.values()) // ?
Map 與其自帶的 keys() 與 values() 亦為典型 iterable object,亦可使用 Array.from() 轉成 array。

Application
由於 Array.from() 的 array-like object 與 map function 特性,因此推導出一些有趣應用。
create()
let create = n => init => Array.from({ length: n }, () => init)
create(3)(1) // ?
若我們想指定特定長度建立 array,且初始值都相同,在 ECMAScript 並沒有內建 function,但我們可以自行建立 create() 完成需求。
因此我們可使用 { length: n } 代表 Array(n),且沒有 sparse array 問題。
from() 的第二個 argument 為 map function,用此建立初始值。

generate()
let generate = n => init => Array.from({ length: n }, (_, i) => i + init)
generate(3)(1); // ?
若我們想指定特定長度建立 array,且給定初始值,隨後會自動建立 sequence,在 ECMAScript 並沒有內建 function,但我們可以自行建立 generate() 完成需求。
因此我們可使用 { length: n } 代表 Array(n),且沒有 sparse array 問題。
from() 的第二個 argument 為 map function,用此建立 sequence。

range()
let range = begin => end => step =>
Array.from({ length: (end - begin)/step + 1 }, (_, i) => begin + (i * step))
range(1)(10)(2) // ?
Ramda 的 range() 只提供 begin 與 end 兩個 argument,但大部分 library 還提供了第三個 argument:step,我們也可以自行實作之。
當牽涉 step 之後,array 的 length 就不只有 n,而是由 begin、end 與 step 共同決定,且 map function 也包含 step。

Conclusion
- ES6 的
Array.from()重要性在於將 ECMAScript 兩個很像 array 的 array-like object 與 iterable object 轉成真正 array,如此就可光明正大使用Array.prototype下豐富 method,不必再如 ES5 使用Function.prototype.call()借用 method Array.from()另一個亮點是可藉由Array.from({ length: n })建立 length 為n的undefinedarray,有別於Array(n)的 sparse arrayArray.from(obj, fn)相當於Array.from(obj).map(fn),且執行速度更快- 由於
Array.from()可藉由 array-like object 轉成 array,又自帶map(),很適合建立有初始值 array,因此推導出create()、generate()與range()等有趣應用