函數柯里化理解示例
// 求和
let add = function (a, b, c) {
return a + b + c
}
{
/* 簡單版curry函數示例 */
// 對求和函數做curry化
let f1 = curry(add, 1, 2, 3)
console.log('簡單版', f1())// 6
// 對求和函數做curry化
let f2 = curry(add, 1, 2)
console.log('簡單版', f2(3))// 6
// 對求和函數做curry化
let f3 = curry(add)
console.log('簡單版', f3(1, 2, 3))// 6
// 但是簡單版curry函數多次調用會報錯,如下:
// console.log('簡單版',f3(1)(2)(3))// Uncaught TypeError: f3(...) is not a function
// 簡單版(只能調用1次)
function curry(fn) {
// 緩存除函數fn之外的所有參數
let args = Array.prototype.slice.call(arguments, 1)
// 閉包
return function () {
// 連接已緩存的老的參數和新傳入的參數(即把每次傳入的參數全部先保存下來,但是並不執行)
let newArgs = args.concat(Array.from(arguments))
// 調用
return fn.apply(this, newArgs)
}
}
}
{
/* 複雜版curry函數示例 */
// 對求和函數做curry化
let f1 = curry(add, 1, 2, 3)
console.log('複雜版', f1())// 6
// 對求和函數做curry化
let f2 = curry(add, 1, 2)
console.log('複雜版', f2(3))// 6
// 對求和函數做curry化
let f3 = curry(add)
console.log('複雜版', f3(1, 2, 3))// 6
// 複雜版curry函數可以多次調用,如下:
console.log('複雜版', f3(1)(2)(3))// 6
console.log('複雜版', f3(1, 2)(3))// 6
console.log('複雜版', f3(1)(2, 3))// 6
// 複雜版(每次可傳入不定數量的參數,當所傳參數總數不少於函數的形參總數時,纔會執行)
function curry(fn) {
// 閉包
// 緩存除函數fn之外的所有參數
let args = Array.prototype.slice.call(arguments, 1)
return function () {
// 連接已緩存的老的參數和新傳入的參數(即把每次傳入的參數全部先保存下來,但是並不執行)
let newArgs = args.concat(Array.from(arguments))
if (newArgs.length < fn.length) {// 累積的參數總數少於fn形參總數
// 遞歸傳入fn和已累積的參數
return curry.call(this, fn, ...newArgs)
} else {
// 調用
return fn.apply(this, newArgs)
}
}
}
}
js bind函數理解示例(使用了柯里化)
// 全局變量
a = 1
// 求和
let add = function (b, c) {
return this.a + b + c
}
{
/* 作爲普通函數直接調用 */
// 此時this指向window,window.a是1
console.log(add(2, 3))// 6
}
{
/* 簡單版bind實現,不具備curry化效果(不支持在傳入對象的同時傳入其他參數) */
Function.prototype.bind = function (obj) {
// 緩存Function構造函數的this(表達式函數聲明時相當於new Function...)
const self = this
// 閉包
return function () {
// 調用
return self.apply(obj, arguments)
}
}
let f1 = add.bind({ a: 0 })
console.log(f1(2, 3))// 5
// 但是簡單版bind實現不支持對象和其他參數同時傳入,如下:
let f2 = add.bind({ a: 0 }, 2, 3)
// 因爲上一行代碼傳入的2、3並沒有接收到,所以求和後返回NaN
console.log(f2())// NaN
}
{
/* 複雜版bind實現,具備部分curry化效果(之所以說部分,是因爲它支持在傳入對象的同時可以其他參數,但是不支持多次調用) */
Function.prototype.bind = function (obj) {
// 緩存Function構造函數的this(表達式函數聲明時相當於new Function...)
const self = this
// 緩存除函數fn之外的所有參數
let args = Array.prototype.slice.call(arguments, 1)
// 閉包
return function () {
// 連接已緩存的老的參數和新傳入的參數(即把每次傳入的參數全部先保存下來,但是並不執行)
let newArgs = args.concat(Array.from(arguments))
// 調用
return self.apply(obj, newArgs)
}
}
let f1 = add.bind({ a: 0 })
console.log(f1(2, 3))// 5
// 複雜版bind實現支持對象和其他參數同時傳入,如下:
let f2 = add.bind({ a: 0 }, 2, 3)
console.log(f2())// 5
// 但是複雜版bind實現多次調用會報錯,如下:
let f3 = add.bind({ a: 0 })
// console.log(f3(2)(3))// Uncaught TypeError: f3(...) is not a function
}
裝飾者模式理解示例(使用了柯里化)
大家看以下代碼,是不是思路都是想通的,詳細的大家就自行拓展吧
// 在之前執行
Function.prototype.before = function (fn) {
let self = this
return function () {
fn.apply(this, arguments)
return self.apply(this, arguments)
}
}
// 在之後執行
Function.prototype.after = function (fn) {
let self = this
return function () {
let res = self.apply(this, arguments)
fn.apply(this, arguments)
return res
}
}
function foo(params) {
console.log(params, 1)
return 22
}
// 鉤子可以共享參數和返回值
console.log(foo.before((params) => {
console.log(params, 2)
}).after((params) => {
console.log(params, 3)
})({ a: 1 }))