javascript高級 --- 函數柯里化

一、介紹
函數柯里化(currying)又稱部分求值,一個currying函數首先接受一部分參數,該函數不會立即求值,而是返回另外一個函數(高階函數),剛纔傳入的參數通過閉包存起來,當真正需要求值時,一次性返回。

二、案例
函數求值
普通函數

function add (x, y) {
	return x + y
}
add(1,2)

我們用柯里化實現

function add(x) {
	return function (x, y) {
		return x + y
	}
}
add(1)(2) // 3

可以看到當我們傳入第一個參數時,會返回一個新的函數去處理剩下的參數

利用箭頭函數會更清晰

let add x => y => x + y
add(1)(2) // 3

三、封裝

我們寫一個柯里化的通用版本

let currying = function (fun) {
	// arguments是一個類數組,需要轉成真實數組
	let args = Array.prototype.slice.call(arguments,1)
	return function () {
		let _args = Array.prototype.sliice.call(arguments)
		fun.apply(this,args.concat(_args))		
	}
}

function add (...args) {
	let result = args.reduce((total,item) => total + item , 0)
	console.log(result)
}

let _add = currying(add,1,2)
_add(3,4) // 10

通過上面代碼我們發現,柯里化之後,_add函數只能執行一次,如果我們想多次執行_add(1)(2)(3),會報錯,因爲我們返回一個值,而不是一個函數。

類似情況,我們首先想到採用遞歸的方式,如果currying之後調用的函數有參數,我們就繼續遞歸執行,反之執行結束

let currying = function (fun, arr = []) {
	// 取出執行時參數,首次執行截掉function
	let args = Array.prototype.slice.call(arguments,1) || arr
	// 閉包 保存結果
	return function () {
		// 再次調用時的參數
		let _args = Array.prototype.slice.call(arguments)
		// 如果參數不存在,執行函數,反之繼續遞歸執行
		if (_args.length == 0){
			return fun.apply(this,args.concat(_args))
		}else {
			return currying.call(this,fun,...args.concat(_args))
		}
	}
}

function add (...args) {
	let result = args.reduce((total,item) => total + item , 0)
	console.log(result)
}

let _add = currying(add)
_add(2)(5)(8)(1)() // 16
_add(2,5,8)(1)() // 16

以上就是函數柯里化的內容了,理解起來比較費勁,編程思想很贊!

注意:

1、arguments是一個類數組(非數組對象),需要用Array.prototype.slice.call(arr)轉成真數組,也可以使用ES6提供的Array.from()處理: Array.prototype.slice.call(arr) === Array.form()
2、ES6規定function可以有length,代表當前函數的參數數量,如果參數設置默認值或者使用擴展運算符,length爲0 (…args / args = [])

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