函数柯里化
函数柯里化
一种函数式编程的思想,柯里化是编程语言中的一个通用的概念,是指把接收多个参数的函数变换成接收单一参数的函数,嵌套返回直到所有参数都被使用并返回最终结果。更简单地说,柯里化是一个函数变换的过程,是将函数从调用方式:f(a,b,c)变换成调用方式:f(a)(b)(c)的过程。柯里化不会调用函数,它只是对函数进行转换。
柯里化应用及特点
参数复用:当在多次调用同一个函数,并且传递的参数绝大多数是相同的,那么就可以用柯里化进行函数的复用,如示例一
提前返回:多次调用内部判断,可以直接把第一次判断的结果返回外部接收
延迟执行:避免重复的去执行程序,等真正需要结果的时候再执行
示例一:(参数复用)
function url_currying(protocol) {
return function(hostname, pathname) {
return `${protocol}${hostname}${pathname}`
}
}
const url_https = url_currying('https://')
console.log(url_https('domain.com', '/dashboard')) // "https://domain.com/dashboard"
示例二:(提前返回)
const addEvent = (function() {
if(window.addEventListener) {
/**
* element: 需要添加事件监听的元素
* type: 为元素添加什么类型的事件
* listener: 执行的回调函数
* useCapture: 要进行事件冒泡或者事件捕获的选择
*/
return function(element, type, listener, useCapture) {
element.addEventListener(type, function(e){
listener.call(element, e) // 用 call 进行 this 的绑定
}, useCapture)
}
} else if(window.attachEvent) {
// IE(IE9 之前) 支持的是 attachEvent 方法
// 不需要第四个参数,因为 IE 支持的是事件冒泡
return function(element, type, listener) {
element.attachEvent('on'+type, function(e) {
listener.call(element, e)
})
}
}
})()
// 测试
let button = document.querySelector('button');
addEvent(button, 'click', (e) => {console.log('点击了 div');}, true);
示例三
/**
* 实现一个 add 方法
* add(1, 2, 3)
* add(1, 2)(3)
* add(1)(2)(3)(4)(5)
*/
function add() {
// 第一次执行时,arguments 对象转化为数组
let args = Array.prototype.slice.call( arguments )
// 内部声明一个函数,利用闭包的特性,保存args,并且收集所有的参数值
let next = function() {
args.push( ...arguments )
return next
}
// 当返回值是一个函数时,自动隐士转换为字符串
// 利用隐士转换的规则,修改toString方法的内部逻辑
next.toString = function() {
return args.reduce(function(total, current) {
return total + current
}, 0)
}
return next
}
console.log( add(1)(2, 5)(3)(4) ) // ƒ () { args.push( ...arguments ) return next }
console.log( typeof add(1)(2, 5)(3)(4) ) // function
console.log( +add(1)(2, 5)(3)(4) ) // 15
/**
* 重写函数 fn 的 toString 方法能够改变函数的隐式转换结果
* 重写函数 fn 的 valueOf 方法也能够改变函数的隐式转换结果
* 同时重写函数的toString方法与valueOf方法时,最终的结果会取valueOf方法的返回结果
*/
function fn() { return 20; }
fn.valueOf = function() { return 60 }
fn.toString = function() { return 30 }
console.log( fn + 10 ); // 70
currying 实现
// currying 原理
// 内部返回一个函数
// 收集参数
// 判断 fn.length 原函数的形参 与 args.length 的长度
// args.length >= fn.length 执行返回
// args.length < fn.length 递归执行 next
function currying(fn) {
return function next() {
let args = Array.prototype.slice.call(arguments)
if(args.length >= fn.length) {
return fn.call(this, ...args)
}
return function(...arg) {
return next(...args, ...arg)
}
}
}
// 测试
function addNum(a, b, c) {
return a + b + c;
}
const addCurry = currying(addNum);
console.log( addCurry(1)(2)(3) ); // 6
console.log( addCurry(1) ); // ƒ (...arg) { return next(...args, ...arg) }
console.log( addCurry(1)(2) ); // ƒ (...arg) { return next(...args, ...arg) }
console.log( addCurry(1)(2, 3) ); // 6
console.log( addCurry(1, 2)(3) ); // 6
console.log( addCurry(1, 2, 3) ); // 6