javascript柯理化函数思想

作者:日期:2017-04-02 21:09:09 点击:1133

【柯里化函数思想(JS高级编程技巧)】

柯里化的定义

  • 柯里化通常也称部分求值,其含义是给函数分步传递参数,每次传递参数后,部分应用参数,并返回一个更具体的函数接受剩下的参数,中间可嵌套多层这样的接受部分参数函数,逐步缩小函数的适用范围,逐步求解,直至返回最后结果。

一个通用的柯里化函数

var curring = function(fn){
var _args = [];
return function cb(){

if(arguments.length === 0) {
return fn.apply(this, _args);
}
Array.prototype.push.apply(_args, [].slice.call(arguments));

return cb;
} }

var multi = function(){

var total = 0;
var argsArray = Array.prototype.slice.call(arguments);
argsArray.forEach(function(item){
total += item;
})

return total
};
var calc = curring(multi);
calc(1,2)(3)(4,5,6);
console.log(calc()); //空白调用时才真正计算

  • 到这里, 不难看出,柯里化函数具有以下特点:

    • 函数可以作为参数传递

    • 函数能够作为函数的返回值

    • 闭包

柯里化函数的作用

函数柯里化允许和鼓励你分隔复杂功能变成更小更容易分析的部分。这些小的逻辑单元显然是更容易理解和测试的,然后你的应用就会变成干净而整洁的组合,由一些小单元组成的组合。

  • 1.提高通用性
function square(i) {return i * i;}
function double(i) { return i *= 2;}
function map(handeler, list) {
return list.map(handeler);
}

// 数组的每一项平方
map(square, [1, 2, 3, 4, 5]);
map(square, [6, 7, 8, 9, 10]);
map(square, [10, 20, 30, 40, 50]);
// ......

// 数组的每一项加倍
map(double, [1, 2, 3, 4, 5]);
map(double, [6, 7, 8, 9, 10]);
map(double, [10, 20, 30, 40, 50]);

创建了一个map通用函数,用于适应不同的应用场景。显然,通用性不用怀疑。同时,例子中重复传入了相同的处理函数:square和dubble。用中这种可能会更多。当然,通用性的增强必然带来适用性的减弱。但是,我们依然可以在中间找到一种平衡。

  • 看下面,我们利用柯里化改造
function currying(fn) {
var slice = Array.prototype.slice,
__args = slice.call(arguments, 1);
return function () {
var __inargs = slice.call(arguments);
return fn.apply(null, __args.concat(__inargs));
}; }
function square(i) {
return i * i;
}
function double(i) {
return i *= 2;
}
function map(handeler, list) {
return list.map(handeler);
}
var mapSQ = currying(map, square);
mapSQ([1, 2, 3, 4, 5]); //[1, 4, 9, 16, 25]

var mapDB = currying(map, double);
mapDB([1, 2, 3, 4, 5]); //[2, 4, 6, 8, 10]

我们缩小了函数的适用范围,但同时提高函数的适性.

  • 2 延迟执行。

    柯里化的另一个应用场景是延迟执行。不断的柯里化,累积传入的参数,最后执行。

  • 3.固定易变因素。

    柯里化特性决定了它这应用场景。提前把易变因素,传参固定下来,生成一个更明确的应用函数。最典型的代表应用,是bind函数用以固定this这个易变对象。

Function.prototype.bind = function(context) {
var _this = this,
_args = Array.prototype.slice.call(arguments, 1);
return function() {
return _this.apply(context, _args.concat(Array.prototype.slice.call(arguments)));
}
}

//Function.prototype.bind 方法也是柯里化应用与 call/apply 方法直接执行不同,bind 方法 将第一个参数设置为函数执行的上下文,其他参数依次传递给调用方法(函数的主体本身不执行,可以看成是延迟执行),并动态创建返回一个新的函数, 这符合柯里化特点。

var foo = { x: 666 };
var bar = function () {
console.log(this.x);
}.bind(foo); // 绑定

bar(); //666

// 下面是一个 bind 函数的模拟,testBind 创建并返回新的函数,在新的函数中将真正要执行业务的函数绑定到实参传入的上下文,延迟执行了。
Function.prototype.testBind = function (scope) {
var self = this; // this 指向的是调用 testBind 方法的一个函数,
return function () {
return self.apply(scope);
}
};

var testBindBar = bar.testBind(foo); // 绑定 foo,延迟执行
console.log(testBindBar); // Function (可见,bind之后返回的是一个延迟执行的新函数)
testBindBar(); // 666

柯里化函数思想:

一个JS预处理思想。通俗的说是利用函数执行返回一个新的函数,其栈内存被占用形成一个不销毁的作用域,存入了部分参数,等待返回的函数再次执行的时候和新的参数进行组合并传入retu函数中执行

函数柯里化方法:

function curry(fn, context) {
context = context || window;
var args = Array.prototype.slice.call(arguments, 2);
return function () {
var innerArgs = Arrray.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
fn.apply(context, finalArgs);
}}

bind 方法重写

Function.prototype.myBind = function (context) {
context = context || window;
var args = Array.prototype.slice.call(arguments, 1);
if('bind' in Funtion.prototype){
fn.bind.apply(this,context,args);
}
var _this = this;
return function () {
var innerArgs = Array.prototype.slice.call(arguments);
innerArgs.length === 0 ? innerArgs[innerArgs.length] = window.e
vent : null;
_this.apply(context, args.concat(innerArgs));
}}

上一篇: javascript策略模式

下一篇: javascript几种变量交换方式及性能分析