ECMAScript Function 的 arguments
是 Array-like Object,僅有部分 Array 功能,實務上我們會希望把 arguments
當成真正 Array 操作。
Version
ECMAScript 2015
Array-Like Object
function f() {
let result = 0
for(let i = 0; i < arguments.length; i++)
result += arguments[i]
return result
}
f(1, 2, 3) // ?
若使用 length
與 []
,則可使用 for loop
實踐任何功能,這也是 arguments
設計為 Array-Like Object 的初衷。
arguments
只有 index、callee
、length
與Symbol
4 個 property其 prototype 為普通 Object 並非 Array,所以沒有
Array.prototype
該有的 method
Array-like Object
有length
property,也可以如同 Array 以 index 操作的 Object,但沒有Array.prototype
該有的 method
Array.prototype.slice()
function f() {
let args = Array.prototype.slice.call(arguments)
return args.reduce((ac, x) => ac + x, 0)
}
f(1, 2, 3) // ?
Array.prototype.slice()
為 Array 自帶的 method,可複製出新 Array,因為內部使用 this
實作,因此可使用 call()
將 arguments
傳入取代內部 this
,如此就可使用 Array.prototype.reduce()
。
[].slice()
function f() {
let args = [].slice.call(arguments)
return args.reduce((ac, x) => ac + x, 0)
}
f(1, 2, 3) // ?
[]
為 Empty Array,因此自帶 slice()
,再以 call()
傳入 arguments
取代 this
,這種寫法更簡短。
Array.prototype.reduce()
function f() {
return Array.prototype.reduce.call(arguments, (ac, x) => ac + x, 0)
}
f(1, 2, 3) // ?
既然目的是要使用 Array.prototype.reduce()
,亦可直接對 reduce()
以 call()
傳入 arguments
。
[].reduce()
function f() {
return [].reduce.call(arguments, (ac, x) => ac + x, 0)
}
f(1, 2, 3) // ?
同理亦可使用 [].reduce()
取代 Array.prototype.reduce()
。
Array.from()
function f() {
let args = Array.from(arguments)
return args.reduce((ac, x) => ac + x)
}
f(1, 2, 3) // ?
ES6 提供了 Array.from()
可將 Array-liked Object 轉成真正 Array,如此可不必使用 Array.prototype.slice()
配合 call()
。
Array Spread
function f() {
let args = Array(...arguments)
return args.reduce((ac, x) => ac + x, 0)
}
f(1, 2, 3) // ?
ES6 的 Array Spread 搭配 Array Constructor Function 亦可將 Array-liked Object 轉成真正 Array。
Function Pipeline
import { pipe, sum } from 'ramda'
import { from } from 'wink-fp'
function f() {
return pipe(
from,
sum
)(arguments)
}
f(1, 2, 3) // ?
若要以 Function Pipeline 風格:
- 使用
from()
將 Array-liked Object 轉成真正 Array - 使用
sum()
計算總和 - 最後使用
pipe()
組合from()
與sum()
Arrow Function
let f = (...args) => args.reduce((ac, x) => ac + x)
f(1, 2, 3) // ?
Arrow function 不再支援 arguments
,必須以 ...
rest parameter 取代。
sum()
import { sum } from 'ramda'
let f = (...args) => sum(args)
f(1, 2, 3) // ?
亦可直接以 sum()
取代 reduce()
。
Point-free
import { unapply, sum } from 'ramda'
let f = unapply(sum)
f(1, 2, 3) // ?
sum()
必須接受 Array,可使用 unapply()
使 sum()
能接受正常 argument,如此 f()
則 Point-free。
Conclusion
- ES5 時代,
Array.prototype.slice.call(arguments)
與[].slice.call(arguments)
都算標準做法,主要是借用slice()
將arguments
轉成新的 array - 也可以直接
借用
Array.prototype
的 method,根本不透過slice()
- ES6 時代使用
Array.from()
與 Array Spread 也都是不錯方式 arguments
在 arrow function 則無法使用arguments
,必須改用 rest parameter 取代- 若要求 Point-free,可直接對
sum()
加以unapply()