函数柯里化
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了函数柯里化相关的知识,希望对你有一定的参考价值。
所谓"柯里化",就是把一个多参数的函数,转化为单参数函数,所有函数只接受一个参数。它是一种"预加载"函数的方法,通过传递部分参数来调用函数,然后让函数返回另一个函数去处理剩下的参数。某种意义上讲,这是一种对参数的"缓存",是一种非常高效的编写函数的方法。
// 柯里化之前 function add(x, y) { return x + y; } add(1, 2); // 3 // 柯里化之后 function addX(y) { return function (x) { return x + y; }; } addX(2)(1); // 3
一、柯里化的作用
1、参数复用;
2、提前返回;
3、延迟计算/运行。
二、举例说明
1、实现curry(fn)(1)(2)(3)
var fn = function(a,b,c){ return a+b+c; }
需要写一个函数,满足curry(fn)(1)(2)(3) //6
var fn = function(a,b,c) { return a+b+c; } function curry(fn) { var arr = [], mySlice = arr.slice; fnLen = fn.length; function curring() { arr = arr.concat(mySlice.call(arguments)); if(arr.length < fnLen) { return curring; } return fn.apply(this, arr); } return curring; }
curry(fn)(1)(2)(3);//6
2、实现sum(1, 2, 3)(10) //16
//方案A var sum = function() { var cache; if (arguments.length === 1) { cache = arguments[0]; return function ( number ) {return cache + number;}; } else return Array.prototype.reduce.call(arguments, function(a,b){ return a+b; }); };
该方法有缺陷,只能计算sum(1, 2, 3, 4),如果计算sum(1, 2)(3, 4),会报错。sum的时候,如何既返回一个值,又返回一个函数以供后续调用?这里牵扯到函数的转换,不难想到valueOf()或是toString()。
下面插播一段儿,定义一个函数,
function test(){ console.log(15); }
如果仅仅调用test而不是test(),会发现test函数被打印了一遍,并没有执行。其实,这里自行调用了函数的valueOf()方法,也就是说,test和test.valueOf()结果相同。valueOf()返回对象的原始值,toString()返回对象的string值,如果二者都不存在,就抛出错误。
//方案B var sum = (function() { var argArr = []; var sumFn = function() { // 拼接数组 var args = Array.prototype.slice.call(arguments); argArr = argArr.concat(args); return sumFn; } // 覆盖 toString 方法 sumFn.toString = function() { // 计算总和 var sum = argArr.reduce(function(pre, next) { return pre + next; }); // 清除记录 argArr.length = 0; return sum; } return sumFn; })();
如果只改写valueOf()或是toString()其中一个,会优先调用被改写了的方法,而如果两个同时改写,则会像Number类型转换规则一样,优先查询valueOf()方法,在valueOf()方法返回的是非原始类型的情况下,再查询toString()方法。
这里,只有最后一次调用才真正调用到toString(),而之前的操作都是合并参数,递归调用本身,由于最后一次调用返回的是一个sumFn函数,所以最终调用了函数的sumFn.toString(),并且利用了 reduce方法对所有参数求和。
//方案C function sum() { // 第一次执行时,定义一个数组专门用来存储所有的参数 var argArr = [].slice.call(arguments); // 在内部声明一个函数,利用闭包的特性保存argArr并收集所有的参数值 var sumResult = function () { var sumFn = function() { [].push.apply(argArr, [].slice.call(arguments)); return sumFn; }; // 利用隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回 sumFn.toString = function () { return argArr.reduce(function (a, b) { return a + b; }); } return sumFn; } return sumResult.apply(null, argArr); }
//方案D function sum(...args) { return args.reduce((a, b) => a + b); }
//方案E function sum() { // 第一次执行时,定义一个数组专门用来存储所有的参数 var argArr = [].slice.call(arguments); // 在内部声明一个函数,利用闭包的特性保存argArr并收集所有的参数值 var sumResult = function () { var sumFn = function() { argArr.push(...arguments); return sumFn; }; // 利用隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回 sumFn.toString = function () { return argArr.reduce(function (a, b) { return a + b; }); } return sumFn; } return sumResult(...argArr); }
/** * 测试用例 */ sum(1)(2) // 3 sum(1, 2, 3)(10) // 16 sum(1)(2)(3)(4)(5) // 15 var a = sum(1)(2)(3)(4); // 10 var b = sum(1, 2, 3, 4); // 10 var c = sum(1, 2)(3, 4); // 10 var d = sum(1, 2, 3)(4); // 10 // 可以利用隐式转换的特性参与计算 a + 10; // 20 b + 20; // 30 c + 30; // 40 d + 40; // 50 // 也可以继续传入参数,得到的结果再次利用隐式转换参与计算 a(10) + 100; // 120 b(10) + 100; // 120 c(10) + 100; // 120 d(10) + 100; // 120
以上是关于函数柯里化的主要内容,如果未能解决你的问题,请参考以下文章