柯里化的实现
面试中经常会遇到这个题目:
写一个函数,实现可多次调用,并且不限制参数个数
add(1, 2, 3, 4); //10
add(1, 2)(3)(4, 5, 6); //21
add(1)(2)(3)(4)(5); //15
最终代码:
function curry(fn) {
// 保存预置参数
const presetArgs = [].slice.call(arguments, 1);
// 返回一个新函数
function curried() {
// 新函数调用时会继续传参
const restArgs = [].slice.call(arguments);
const allArgs = [...presetArgs, ...restArgs];
return curry.call(null, fn, ...allArgs);
}
// 重写toString
curried.toString = function () {
return fn.apply(null, presetArgs);
};
return curried;
}
function dynamicAdd() {
return [...arguments].reduce((prev, curr) => {
return prev + curr;
}, 0);
}
var add = curry(dynamicAdd);
const val = add(1)(2)(3)(4);
alert(val); //10
const val2 = add(5)(10, 20, 40, 15);
alert(val2); //90
实现原理主要是运用了以下方法:
- 闭包
- 重写 toString()方法
- call,apply 更改函数 this 指向
思路
使用 argments 接收参数,然后利用闭包的特性,所以在返回的函数当中储存的那个 presetArgs 始终是上一次运行函数时传入的参数列表,也就是说在调用 add(1)的时候,函数中的 presetArgs 其实什么都没有,是个空数组。在调用 add(1)(2)的时候,presetArgs 的值保存了两个,一个是[],一个是[1],所以他才能在多次调用的时候一直都能往 presetArgs 里面塞参数,并且由于闭包,返回的函数里面的那个 fn 其实一直指向的都是 dynamicAdd 这个传入的参数。最后都是处理数据的永远都是这个函数,使用apply是改变传入的方法的this指向,从而进行调用。
为什么要重写返回的函数的toString?
直接打印函数的话会输出该函数的函数体,运用alert()方法会将函数体隐式转换成字符串的思路来重写toString方法,从而在实现传入多少个值最后都会输出计算结果
tips: 高版本chrome浏览器已经不会对console.log里面参数进行字符串的隐式转换了