理解 javascript 函数中的 curry
函数柯里化: 所谓函数柯里化就是把一个函数的多个传参变成多个函数的单个传参。 函数的柯里化,是 Javascript 中函数式编程的一个重要概念。它返回的,是一个函数的函数。其实现方式,需要依赖参数以及递归,通过拆分参数的方式,来调用一个多参数的函数方法,以达到减少代码冗余,增加可读性的目的。fn(a,b,c) 变成 curry(a)(b)(c)
本质: 实现多个参数合并为一个参数数组,apply 调用 函数。
f1 传参a 中 return 一个匿名函数 传参b , 匿名函数在在return 一个 函数执行。这样的好处是可实现参数的复用。
function sum(a, b, c) {
console.log(a + b + c);
}
const fn = curry(sum);
fn(1, 2, 3); // 6 ==>sum args: [3,2,1]
fn(1, 2)(3); // 6 ==>sum args: [3,1,2]
fn(1)(2, 3); // 6 ==>sum args: [2,3,1]
fn(1)(2)(3); // 6 ==>sum args: [3,2,1]
function curry (fn, currArgs) {
return function() {
let args = [].slice.call(arguments);
console.log('ARGS: ',args);
// 首次调用时,若未提供最后一个参数currArgs,则不用进行args的拼接
if (currArgs !== undefined) {
args = args.concat(currArgs);
}
// 递归调用 fn,length 代表函数传参个数。
if (args.length < fn.length) {
return curry(fn, args);
}
// 递归出口
return fn.apply(null, args);
}
}
curry 第一个传参为函数sum ,第二个传参为fn (fn 代表被curry 函数包裹传参fn 后返回的函数)函数的参数currArgs.
后可以通过 fn(1)(2)(3) 传参,curry 函数通过递归实现了后面参数与currArgs拼接,拼接后的 参数个数与fn 函数传参相同,顺序由不同的传参方式不同而不同。
loadash.js 中正是运用柯里化函数。用于快速返回数组中某一个属性的值。
const persons = [
{ name: 'kevin', age: 4 },
{ name: 'bob', age: 5 }
];
// 这里的 curry 函数,之前已实现
const getProp = curry(function (obj, index) {
const args = [].slice.call(arguments);
return obj[args[args.length - 1]];
});
const ages = persons.map(getProp('age')); // [4, 5]
const names = persons.map(getProp('name')); // ['kevin', 'bob']
由于map 函数的 回调中自动传递相关参数,相当于 getProp('age')(currentValue,index,arr) ==> 参数 【curentValue,index,arr,age】因此,fn 的传参个数必须大于等于2,才能 return obj[attr]
ARGS: [ 'age' ] no currentArgs
ARGS: [ { name: 'kevin', age: 4 },0, [ { name: 'kevin', age: 4 }, { name: 'bob', age: 5 } ] ]
concat args: [ { name: 'kevin', age: 4 },0, [ { name: 'kevin', age: 4 }, { name: 'bob', age: 5 } ], 'age' ]
ARGS: [ { name: 'bob', age: 5 },1, [ { name: 'kevin', age: 4 }, { name: 'bob', age: 5 } ] ]
concat args: [ { name: 'bob', age: 5 },1,[ { name: 'kevin', age: 4 }, { name: 'bob', age: 5 } ], 'age' ]
var new_array = arr.map(function callback(currentValue[, index[, array]]) {
// Return element for new_array
}[, thisArg]);
对于优化比较耗时的计算,可以利用缓存,利用闭包,将结果缓存到内存中。
嵌套在函数作用域中的函数,称为闭包函数。该作用域称为闭包环境;通过闭包函数可以访问闭包环境所在的作用域变量与形参,此利用了javascript 的变量回收机制,一个函数调用时开辟空间,函数结束调用,释放空间,垃圾回收机制释放调用结束的函数时 ,发现函数的变量正在其他地方调用,这些变量不会释放,永久保存在内存。只有退出程序,才会释放。
function memoizeFunction(func) {
const cache = {};
return function() {
let key = arguments[0];
if (cache[key]) {
return cache[key];
} else {
const val = func.apply(null, arguments);
cache[key] = val;
return val;
}
};
}
const fibonacci = memoizeFunction(function(n) {
return (n === 0 || n === 1) ? n : fibonacci(n - 1) + fibonacci(n - 2);
});
console.log(fibonacci(100)); // 输出354224848179262000000
JavaScript 经典面试题
// 实现一个add方法,使计算结果能够满足如下预期:
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;
function add() {
// 第一次执行时,定义一个数组专门用来存储所有的参数
var _args = Array.prototype.slice.call(arguments);
// 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
var _adder = function() {
_args.push(...arguments);
return _adder;
};
// 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
_adder.toString = function () {
return _args.reduce(function (a, b) {
return a + b;
});
}
return _adder;
}
add(1)(2)(3) // 6
add(1, 2, 3)(4) // 10
add(1)(2)(3)(4)(5) // 15
add(2, 6)(1) // 9
偏函数: 固定一个函数的一个或多个参数,也就是将一个n元函数转换成一个n-x元函数。
使用场景
- 动态生成函数
- 减少参数
- 延迟计算