通过手写柯里化函数,可以充分理解闭包的使用场景,arguments伪数组,以及一些关于原型的底层知识。
比如实现一个功能,给到这么一个函数,将所有参数相加最终返回来执行结果(此函数返回结果为55)
currying(1, 2, 3)(4, 5, 6)(7, 8, 9, 10),如该怎么做呢,仔细分析需要做到以下几点?
- 一个函数可以接收多个参数,被反复调用
- 是一个参数个数不固定的函数
- 每次执行应该是返回一个函数供后续调用
- 参数缓存(即前面的参数会缓存到最后,不会被后面函数调用时的参数覆盖掉)
通过以上分析,代码实现如下
function currying(){
//arguments是伪数组,有数组的属性却不包含数组的方法,通过以下方式将其改变成一个真正的数组
//slice是数组原型上的方法,不传参时会返回一个新数组
//apply(或call)改变this指向
//数组方法依赖于内部this数据容器来执行,因此传入arguments会返回一个新数组
const args = Array.prototype.slice.apply(arguments)
const inner = function inner(){
args.push(...arguments) //此处与父函数形成闭包,用于缓存参数
return inner //每次调用返回inner函数,才可以重复调用
}
return inner //第一次调用返回inner函数
}
此时已经实现了参数缓存以及函数的连续调用。那如何返回计算的结果呢?因为inner函数必须返回一个函数才能连续调用,而JS函数里却只能有一个return。此时可以给inner函数添加一个自定义的getValue方法,如下:
function currying(){
const args = Array.prototype.slice.apply(arguments)
const inner = function inner(){
args.push(...arguments)
return inner
}
inner.getValue = ()=>{
console.log(args) // [1,2,3,4,5,6,7,8,9,10]
return args.reduce((pre, cur)=> pre + cur, 0)
}
return inner
}
const res = currying(1, 2, 3)(4, 5, 6)(7, 8, 9, 10);
console.log(res.getValue()) // 55
以上基本上实现了一开始提到的几个功能,不过如果想要允许函数有返回值进行二次计算,比如想得到 res - 1 的值就得额外调用一个getValue方法,用res.getValue() - 1 替代,所以可以进行以下改造:
function currying(){
const args = Array.prototype.slice.apply(arguments)
const inner = function inner(){
args.push(...arguments)
return inner
}
inner.__proto__[Symbol.toPrimitive] = function (){
return args.reduce((pre, cur)=> pre + cur, 0)
}
return inner
}
const res = currying(1, 2, 3)(4, 5, 6)(7, 8, 9, 10);
console.log(res - 1)