进来了吗? 应该是进来了。 by the way,这里没有六脉神剑,甚至和六脉神剑八杆子打不着边,可这篇博文的标题就叫这个,不改了,况且改成什么?
回正题, javascript 中的函数柯里化,网上很多文章都有写,五花八门,甚至不乏奇技淫巧。(实际工作大家中用的很少吧)笔者看了一些,原本我认为对它还算了解,看着看着越迷糊了,于是整理花了点时间简单整理了一下,希望对大家有点帮助。
权威出处
函数柯里化(function currying),它用于创建已经设置好了一个或多个参数的函数。函数柯里化的基本方法和函数绑定是一样的:使用一个闭包返回一个函数。两者的区别在于,当函数被调用时,返回的函数还需要设置一些传入的参数。
function add(num1, num2){
return num1 + num2;
}
function curriedAdd(num2){
return add(5, num2);
}
alert(add(2, 3)); //5
alert(curriedAdd(3)); //8
这段代码定义了两个函数:add()和 curriedAdd()。后者本质上是在任何情况下第一个参数为 5 的 add()版本。尽管从技术上来说curriedAdd()并非柯里化的函数,但它很好地展示了其概念。
柯里化函数通常由以下步骤动态创建:调用另一个函数并为它传入要柯里化的函数和必要参数。下面是创建柯里化函数的通用方式。
function curry(fn){
var args=Array.prototype.slice.call(arguments, 1);
return function(){
var innerArgs=Array.prototype.slice.call(arguments);
var finalArgs=args.concat(innerArgs);
return fn.apply(null, finalArgs);
};
}
以上内容来自于 《javascript高级程序设计》 红皮书。
注意加粗部分 curriedAdd() 并非柯里化函数
拓展A
来源一
某面试题,请实现一柯里化函数
然后原博贴了这么一段代码
const curry = (fn, ...args1) => (...args2) => (
arg => arg.length === fn.length ? fn(...arg) : curry(fn, ...arg)
)([...args1, ...args2]);
// 调用
const foo = (a, b, c) => a * b * c;
curry(foo)(2, 3, 4); // -> 24
curry(foo, 2)(3, 4); // -> 24
curry(foo, 2, 3)(4); // -> 24
curry(foo, 2, 3, 4)(); // -> 24
看到上面的代码,必须来一句 "卧槽" 箭头函数原本可用来简化函数书写,这上面连续3箭头看得我有点晕乎,最后面括号内的参数到底传给谁了? 把它搞的好看一点
const curry = function(fn, ...args1){
return function (...args2) {
return function(arg){
return arg.length === fn.length ? fn(...arg) : curry(fn, ...arg)
}([...args1, ...args2]);
}
}
常规的通用柯里化函数只是函数里返回函数,这个是函数里返回函数,里面的函数再返回一函数,这最内部函数直接在中间函数被调用了。
这个通用curry 函数的原理类似递归调用,通过判断参数个数。 假如原始函数 fn 有8个参数,只要传递的参数不够8个,它就继续递归调用自身,够,就调用原始函数 fn
拓展B
来源二
实现一个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;
}
以上内容代码完全引用自"来源二",下面是我个人的实验:
有点意思,但我们用如下方式调用它时,确实能达到预期效果
console.log("add(1)(2)(3):",add(1)(2)(3));
console.assert(add(2, 6)(1)!=9, "add(2, 6)(1)!=9");
console.log(add(1, 2, 3)(4)=="10")
console.log(add(1, 2, 3)(4)==10)
console.log(add(1, 2, 3)(4)==="10")
输出结果为:
add(1)(2)(3): 6
Assertion failed: add(2, 6)(1)!=9
true
true
false
貌似又不太严格。 add 明显是一个函数,如果调用它,它返回的其实也是一个内部函数,只是这个内部函数重写了 toString 从而使得在一些默认的转换处得到了预期数值。 与之对应的有 valueOf
再在原 add 函数中增加如下代码
_adder.valueOf = function () {
return 0;
}
那么,你认为如下代码的输出是什么?
console.log(add(1, 2, 3)(4))
console.log("hello boy:",add(1, 2, 3)(4))
console.log(add(1, 2, 3)(4)=="10")
console.log(add(1, 2, 3)(4)==10)
...思考中...
四行结果分别是:
{ [Function: _adder] toString: [Function], valueOf: [Function] }
hello boy: 10
false
false
----有没有惊喜?
所以
有没有收获
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。