在函数式编程中,函数柯里化是指将接受多个参数的函数转为接受单个参数的函数。
背景基础知识
1)函数的length属性--返回定义时的函数参数个数
// 应用:可以用来判断函数定义时和调用时的参数差异function test(a,b,v) { // ...}test(1,2,3,4,5)console.log(test.length === 3) // true// 实际参数长度5,定义时参数长度为3
2) 函数的arguments对象:函数调用时传入的参数,与函数声明的参数无关
function a() { console.log(Array.prototype.slice.call(arguments, 1)) // [2,3,4]}a(1,2,3,4);
- 获取函数的所有参数;
- 类数组的对象({0: value, 1: value, 2: value, ..., length: N}),有需要时可以通过slice方法转为数组
- 非严格模式可以修改arguments的值,严格模式无效
- 非严格模式下,callee属性可以返回函数本身,用于自身调用;严格模式无效
- length属性,表示实际调用参数的个数;与定义时的参数个数无关。
function fn(a, b) { console.log(arguments.length); // 3 console.log(arguments.callee === fn) // true // 转为数组 console.log(Array.prototype.slice.call(arguments)); // [1,2,3] arguments[0] = 5; arguments[1] = 6; return a + b;}fn(1,2,3); // 11
3) valueOf() 和toString() 方法
由下面的运行结果可以指导,返回函数赋值的变量时,会默认调用函数的toString()方法;
⚠️:下面的运行结果都是在chrome浏览器的控制台的运行结果;firefox浏览器结果不同
⚠️上图的程序中最后只调用了add函数,所以直接返回fn,不会进入调用fn;
应用:
柯里化的实现涉及到了闭包,递归,arguments的存取
1.题目要求:(当目标函数参数个数固定的时候, 将已知函数转换成柯里化函数)
function add(a,b,c) { return a+b+c; } // 目标:将add改为通用的柯里化函数,使得下面运算结果和add相同 // 并不是要重新写一个函数,只是修改传参形式,最后还是调用的原来的函数
_add(a)(b)(c); _add(a)(b,c); _add(a,b)(c); //---> return a+b+c /* ->createCurry(add)(a,b)(c)-> createCurry(add, a,b)(c)\ ->createCurry(add,a,b,c) -> add(a,b,c); 从上面可以看到,createCurry是一个收集参数的过程;直到收集到函数定义时的参数个数 */
柯里化函数:
// 通用的柯里化创建函数如下 function createCurry(fn, args) { // args用于表示已经收集到的参数 const arity = fn.length; // 定义时函数fn的参数个数 const collectedArgs = args || []; // 第一次为undefined return function() { const _args = Array.prototype.slice.call(arguments); const allargs = _args.concat(collectedArgs); // 到此为止收集到的所有的参数 const argsLength = allargs.length; if (argsLength < arity) { // 收集的参数个数还
2. 通用柯里化函数(以上面的题目为例,如果参数不固定);
function add() { var args = Array.prototype.slice.call(arguments); // 存储收集的参数 var fn = function() { // 函数定义,不调用就不会执行; var newArgs = args.concat(Array.prototype.slice.call(arguments)); return add.apply(this, newArgs); // 回调停止条件是根据调用情况,不调用就停止 } fn.toString = function() { return args.reduce(function(a, b) { return a + b; }) } return fn;}console.log(add(1)(2)(3)(4)) // f 10 function类型console.log(add(1)(2)) // f 3 function类型Number(add(1)(2)) --- 3
优缺点:
1.优点:
1)可以实现参数复用;
2)实现延迟执行;先收集所有参数,最后执行;
3)固定易变参数
Function.prototype.bind = function() { const args = Array.prototype.slice.call(arguments); const context = args.shift(); // 第一个参数是绑定的对象; const _this = this; return function() { const allArgs = args.concat(Array.prototype.slice.call(arguments)); return _this.apply(context, allArgs); }}function fn() { return this.a;}const obj = { a : 3 };var newFn = fn.bind(obj)(3);console.log(newFn); // 3
缺点:
1. 闭包占用大量内存,产生性能问题
2.argument存取效率低
3. 递归效率低