以前電面有問到:「你知道一個函數的length是什麼嗎?」前端
由於沒看過,也沒碰到過使用場景,我沒答出來。後來查了下發現是指函數的參數個數,因而也就做罷了。不過今天恰巧碰到了使用場景,且發現以前的理解也有誤,因而就寫一篇短文分享一下。數組
今天在嘗試 render props 的各類擴展玩法,寫了個簡單的表格數據 CRUD 操做 Demo:CodeSandboxbabel
寫Demo通常從全部操做都同步開始,最後再全改爲異步的狀況。因此須要寫一個將全部同步函數(數據加載以及增刪改)都轉換成異步函數的工具函數。我一開始寫得以下:dom
const delay = ms => new Promise(_ => setTimeout(_, ms));
// 打算從今開始儘可能使用 async/await
const withRequest = func => async args => {
await delay(1000);
if (Math.random() > 0.3) {
func(...args);
} else {
message.info("操做失敗!");
}
};
// 等一秒以後 30% 失敗,70% 執行操做
複製代碼
這是個所謂的 Curried(庫裏?柯里?咖喱?)函數,用於批量改造函數的函數,接受func爲參數,返回改造好的func。明眼人應該已經發現錯在哪裏了,不過我沒有,因而走了一堆彎路,卻收穫很多。異步
用此函數包裹了個人一堆測試方法:async
add = (a,b) => a + b
square = a => a * a
loadData = () => this.setState({ ... })
loadData = withRequest(this.loadData);
add = withRequest(this.add);
square = withRequest(this.square);
複製代碼
立馬報錯跪了,因而我知道在沒有參數的 loadData 函數那裏跪了,並開始了個人求知之旅。函數
如何將任意個參數從上級函數傳遞給下級函數?工具
分狀況討論的關鍵是:如何知道函數有幾個參數呢?毫無疑問我想到了fn.length
, 因而寫下:post
const len = func.length
if(len === 0){
func()
} else if(len === 1) {
func(args)
} else {
func(...args)
}
複製代碼
這對了嗎?答案是不對。 fn.length
的定義是:函數的形參個數。也就是函數定義時的參數個數,而不是函數實際接受的參數個數。好比測試
const add = (a,b) => a + b
add(1,2,3,4,5) // 3
add.length // 2
複製代碼
而問題的狀況,咱們須要判斷的是函數接受的參數個數。這時候有一個方便的內置變量:arguments
function func1(a, b, c) {
console.log(arguments[0]); // 1
console.log(arguments[1]); // 2
console.log(arguments[2]); // 3
}
func1(1, 2, 3);
複製代碼
arguments 即爲函數接收到的全部參數組成的(類)數組。那麼用 arguments.length
替換全部 func.length
是否就對了呢?仍是不對,arguments 有它的侷限性:
const func1 = (a, b, c) => {
console.log(arguments[0]); // 1
console.log(arguments[1]); // 2
console.log(arguments[2]); // 3
}
func1(1, 2, 3);
// error: arguments is not defined
複製代碼
箭頭函數沒有arguments。 同時注意到如今前端代碼的箭頭函數會通過 babel 轉譯,產生的結果是 arguments 雖然不會 undefined,但會有各類怪異賦值。總之在箭頭函數裏別使用。
剩餘參數 (Rest parameters)
const withRequest = func => async (...args) => {
await delay(1000);
if (Math.random() > 0.3) {
func(...args);
} else {
message.info("操做失敗!");
}
};
複製代碼
這段代碼裏出現了兩個 ...args
, 前者是剩餘參數,後者是數組展開。二者一個是收束,一個是展開。功能相反。
function fun1(...args) {
console.log(args.length);
}
fun1(); // 0
fun1(5); // 1
fun1(5, 6, 7); // 2
複製代碼
剩餘參數語法將「剩餘」的參數收束到一個數組中, 注意和 arguments 不同, 剩餘參數是一個真正的數組。繞了一大圈,實際上是忘記寫了三個點。不過也算真正理解了:
最後,讓咱們用剩餘參數挑戰一個實用函數吧:
寫一個callAll
函數,它接收任意數量的函數和任意數量的參數,若是做爲參數的函數存在就用全部的參數調用那個函數。
const add = (a,b) => {console.log(a + b)}
const minus = (a,b) => {console.log(a - b)}
callAll(add, minus)(2,1)
// 3
// 1
複製代碼
答案以下:
// 剩餘參數是一個真正的數組,可使用任何數組方法
const callAll = (...fns) => (...args) => fns.forEach( fn => fn && fn(...args))
複製代碼