面試題:實現add(1)(2)(3) //結果 = 6,題的核心就是問的js的柯里化面試
先說說什麼是柯里化,看過許多關於柯里化的文章,始終搞不太清楚,例如:柯里化(Currying)是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,而且返回接受餘下的參數且返回結果的新函數的技術。大多數的blog都是這種說法,說實話我是懵逼的。 個人理解是,curry是一個收集參數的方法,收集夠了去執行函數。segmentfault
實現前咱們先列一下要點 一、收集參數(就像面試題屢次執行多個參數)是的利用閉包 二、每次執行參數有多有少例如add(1)(2,3)(4) 三、咱們須要知道何時參數夠了數組
//如題
//add(1)(2)(3)
//邏輯應該是這樣add(1)執行收集參數1繼續執行收集參數2依次類推直到收集完畢。
function curry(fn) {
let arg = []; //用於收集參數
//作一個閉包https://segmentfault.com/a/1190000017824877
return function() {
//每執行一次收集一次參數,爲何用concat是由於有時候後是多個參數(2,3)
arg = arg.concat([...arguments]);
//直到參數收集完成執行fn
// 咱們須要知道何時收集完了,條件就是curry參數fn的參數個數 fn.length
//若是收集的參數個數大於等於fn的參數個數執行fn,若是沒有遞歸執行
if (arg.length >= fn.length) {
return fn(...arg)
}
// 參數沒有收集完咱們須要繼續收集,遞歸
return arguments.callee
}
}
// 測試一下
let testAdd = curry(add1)
// console.log(testAdd(1)(2)(3))
// console.log(testAdd(1, 2)(3))
//console.log(testAdd(1)(2, 3))
複製代碼
一不當心寫完了!bash
不過不能標題黨,說好的詳解,接下來咱們解析一下網上大多數柯里化的實現代碼閉包
function curry(fn) {
function _c(restNum, argsList) {
return restNum === 0 ?
fn.apply(null, argsList) :
function(x) {
return _c(restNum - 1, argsList.concat(x));
};
}
return _c(fn.length, []);
}
複製代碼
一眼看不明白沒事,咱們多看幾眼。 解析: 一、curry也是接收一個參數(fn)這個是必然 二、返回了一個函數,接收兩個參數,fn.length和一個空數組這個好解 釋,我寫的簡版也說過了,fn.length是爲了判斷參數是否收集夠 了,參數傳一個空數組其實也是閉包的一種實現,用來收集參數。 三、裏邊是一個三目判斷,看着花裏胡哨的沒那麼複雜,判斷fn的參數個數若是是0,那就不必收集了直接執行fn,至於fn.apply(null,argList)我很明白的大聲說出來會用個apply就處處用嗎?在我看來沒有一分錢用,之因此用是由於argList是一個數組,正好apply正好支持第二個參數是數組,主要看起來很牛逼的樣子。 四、收集參數,fn參數個數不爲零,每次收集fn函數參數的個數減一,直到等於0執行fn,這個就沒有我寫的通用了,我一次傳倆就掛了。app
再來一個例子:bind方法實現函數
Function.prototype.bind = function(context) {
//返回一個綁定this的函數,咱們須要在此保存this
let self = this
// 能夠支持柯里化傳參,保存參數
let arg = [...arguments].slice(1)
// 返回一個函數
return function() {
//一樣由於支持柯里化形式傳參咱們須要再次獲取存儲參數
let newArg = [...arguments]
console.log(newArg)
// 返回函數綁定this,傳入兩次保存的參數
//考慮返回函數有返回值作了return
return self.apply(context, arg.concat(newArg))
}
}
// 搞定測試
let fn = Person.say.bind(Person1)
fn()
fn(18)
複製代碼
是的bind方法就是用的柯里化,bind實現詳情請移步:segmentfault.com/a/119000001…測試