javascript柯里化

1.什么是柯里化?

柯里化概念其实很简单,只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。

实例:

function add(a, b, c, d) {
    return a + b + c + d;
}
//通常调用方式
var sum = add(1, 2, 3, 4);
//柯里化的调用方式
var curryAdd = Curry(add);
var sum = curryAdd(1)(2)(3)(4);
//或者很多奇怪的方式调用
var sum = curryAdd(1, 2)(3, 4);
var sum = curryAdd(1, 2, 3)(4);
var sum = curryAdd(1)(2, 3, 4);
//...诸如此类

2.分析

2.1 柯里化简化版本分析

首先我们考虑一种比较简单的情况:两次调用以内必须传递函数所需的所有参数。

//诸如此类调用方式
var curryAdd = Curry(add);
var sum = curryAdd(1,2)(3,4);
var sum = curryAdd(1,2,3)(4); 
//  ...

难点:

  1. 预存储第一次调用的参数
  2. 第二次调用时,两次传入的参数合并

因为需要调用两次,因此subCurry需要返回一个函数(思想类似与jq的链式调用)

function subCurry (fn) {
    //取出第一次调用时传入的参数
    //取出除了fn之外的参数
    var args = [].slice.call(arguments, 1);
    
    //第二次调用
    return function () {
        //参数拼接
        //args.concat([].slice.call(arguments,0))
        //由于事先规定两次调用参数必须传递达到fn调用需求
        //因此直接调用函数fn
        return fn.apply(this, args.concat([].slice.call(arguments,0)));
    }
}

function add (a, b, c, d) {
    return a + b + c + d;
}
var myAdd = subCarry(add, 1 , 2)
console.log(myAdd(3, 4)) //10

2.2 柯里化全面版本

//获取形参个数的方式: 函数名.length
//获取实参个数的方式:arguments.length
function curry (fn, length) {
    length = length || fn.length;
    //如果实参个数等于满足函数调用的形参个数
    //执行函数
    return function () {
    	if (arguments.length >= length) {
	        return fn.apply(this, arguments);
	    }else { 
	        //如果实参个数不等于满足函数调用的形参个数
	        //因此需要再次传递参数,直至达到要求
	        
	        //那么再一次传递参数,要执行什么样的流程那?
	        //1. 新传入的参数和已经传入参数进行拼接
	        //   subCurry
	        //2. 拼接之后再次检查是否达到要求
	        //   curry
	        //3. 如果达到要求,执行函数
	        //4. 如果没有达到要求,再次执行1.2.3步骤(递归)
	        var combined = [fn].concat([].slice.call(arguments, 0));
	        return curry(subCurry.apply(this, combined), length - arguments.length);
	    }
    }
}

3.原生JS模拟bind实现

  1. bind特点
    • 返回一个函数
    • 可以传入参数

bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于MDN)

  1. 实例
var a = 1;
var obj = {
    a: 2,
    add: function (x, y) {
        console.log(this.a + x + y);
    }
}
var add = obj.add;
add(2, 3)//6
var newAdd = add.bind(obj, 2);
newAdd(3)//7 

var newAdd2 = add.bind(obj)
newAdd2(2, 3)//7 

是不是感觉上述代码的调用方式有点眼熟?

很明显,就是subCurry的思想。

直接上代码

Function.prototype.myBind = function (context) {
    //预存储第一次传入的参数
    var self = this;
    var args = [].slice.call(arguments, 1);
    return function () {
        var combined = args.concat([].slice.call(arguments,0));
        self.apply(context, combined);
    }
}

除上述特点之外,bind还有一个比较麻烦的特点,更完善的bind原生模拟实现见《call,apply,bind深入了解》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章