柯里化
是一个将使用多个参数的函数转换成一系列使用一个参数的函数的技术。
用处
前端使用柯里化的用途主要是简化代码结构,提高系统的维护性,一个方法,只有一个参数,强制的功能的单一性,很自然就做到了功能内聚,降低耦合。
- 柯里化优点就是降低代码的重复,提高代码的适应性。
特性
- 参数复用
- 提取确认
- 延迟运行(bind 函数节流)
缺点
- 存取arguments对象通常要比存取命名参数要慢一点
- 一些老版本浏览器在arguments.length的实现上是相当慢的
- 闭包函数中的变量都保存在内存中 内存消耗大
实现柯里化
期望
function add(a,b,c){}
let newAdd = curry(add)
// 可随意传 但参数个数固定
newAdd(1)(2)(3)
newAdd(1,2)(3)
newAdd(1)(2,3)
newAdd(1, 2, 3)
实现
// 传入固定参数个数函数 length参数个数
// 期望尽可能把参数传完整
/**
* 柯里化函数
* @param {Function} fun 要柯里化的函数
* @param {Number} length 参数个数
*/
function Curry(fun, length) {
let len = length || fun.length // 参数个数
return function () {
// 判断参数个数是否足够
let plen = arguments.length
let paramsArr = [].slice.call(arguments, 0)
if(plen === len){
return fun.apply(this, paramsArr)
}else{
// 传入的参数小于固定参数 [fun, 1]
let combind = [fun].concat(paramsArr)
// 把当前函数 柯里化 参数个数已确定
// FixedCurry.apply(this, combind) => function
// len-plen 下一次柯里化的 期望参数个数
return Curry(FixedCurry.apply(this, combind), len - plen)
}
}
}
// 固定参数科里化
function FixedCurry(fun) {
let arg1 = Array.prototype.slice.call(arguments, 1)
return function () {
let arg2 = Array.prototype.slice.call(arguments, 0)
return fun.apply(this, arg1.concat(arg2))
}
}
测试
function add(a, b, c, d){
return a + b + c + d
}
let newAdd = Curry(add)
console.log(
newAdd(1,2,3,4),
newAdd(1,2)(3,4),
newAdd(1)(2,3,4),
newAdd(1)(2,3)(4),
newAdd(1)(2)(3)(4),
newAdd(1)(2,3,4),
)
// 10 10 10 10 10 10
ES6 实现
function Curry(fun, ...args) {
return function (...args2) {
const newArgs = [...args, ...args2]
// 判断参数个数是否足够
if(newArgs.length >= fun.length){
return fun(...newArgs)
}
return Curry(fun, ...newArgs)
}
}
function add(a, b, c, d) {
return a + b + c + d
}
const newAdd = Curry(add)
console.log(newAdd(1,2)(3,4))
console.log(newAdd(1)(2)(3,4))
console.log(newAdd(1)(2)(3)(4))
示例
function ajax(type, url, data, cb){
let xhr = new XMLHttpRequest()
xhr.open(type, url)
xhr.onload = function(){
if(xhr.status = 200){
cb && cb(xhr.reponseText)
}
}
xhr.send(data)
}
虽然ajax这个函数非常通用 但在重复调用的时候冗余
ajax('POST', 'www.test1.com', 'name=张三', data=>console.log(data))
ajax('POST', 'www.test2.com', 'age=18', data=>console.log(data))
ajax('POST', 'www.test3.com', 'sex=0', data=>console.log(data))
// ...
利用柯里化
let curryAjax = Curry(ajax)
// 以post方式请求
let post = curryAjax('POST')
// 请求 'www.test1.com'
post('www.test1.com','name=张三',data=>{
console.log(data)
})
post('www.test2.com', 'age=18', data=>{
console.log(data)
})
post('www.test3.com', 'sex=0',data=>{
console.log(data)
})
逐步分解
let curryAjax = Curry(ajax)
// 以post方式请求
let post = curryAjax('POST')
// 请求 'www.test1.com'
let test1 = post('www.test1.com')
// 发送参数
let param = test1('name=张三',data=>{
console.log(data)
})
可将重复部分分解 简化代码
ajax('POST', 'www.test1.com', 'name=张三', data=>console.log(data))
ajax('POST', 'www.test1.com', 'name=15', data=>console.log(data))
ajax('POST', 'www.test1.com', 'name=16', data=>console.log(data))
let curryAjax = Curry(ajax)
// 以post方式请求 'www.test1.com'
let postTest1 = curryAjax('POST', 'www.test1.com'))
postTest1('name=张三',data=>{
console.log(data)
})
postTest1('name=15',data=>{
console.log(data)
})
postTest1('name=16',data=>{
console.log(data)
})
反科里化
就是扩大方法的适用范围
- 可以让任意对象拥有其它对象的方法
- 增加被反科里化方法接收的参数
function uncurring(fn){
return function(){
let args = [].slice.call(arguments, 1)
return fn.apply(arguments[0], args)
}
}
let push = uncurring(Array.prototype.push)
let obj = {}
push(obj, 'newValu')
console.log(obj)
Function.prototype.uncurring3 = function(){
var _this = this
return function(){
return Function.prototype.call.apply(_this, arguments)
}
}
let obj = {}
let push = Array.prototype.push.uncurring3()
push(obj,'value')
console.log(obj)