js里面的函数绑定和函数柯里化

js里面的函数绑定和函数柯里化

函数绑定

函数绑定创建一个函数,可以在特定的this环境中以指定参数调用另一个函数,常常和回调函数与事件处理程序一起使用,以便在将函数作为变量传递的同时保留代码执行环境。一个简单的bind()函数接受一个函数和一个环境,并返回一个在给定环境中调用给定函数的函数,并且将所有参数原封不动的传递过去。

  • 基本语法(ES5定义了原生的bind方法,所以一般不用自己定义):
function bind(fn, cotext){//(函数, 上下文环境)
	return function(){
		return fn.apply(context, arguments);
	};
}
  • 样例:
var handler = {
	message: "Event handled",
	handleClick: function(){
		alert(this.message);
	}
};
var btn = document.getElementById("my_btn");
btn.addEventListener("click", handle.handleClick, true);//点击后返回undefined

样例中按钮点击后返回undefined,是因为this是指向btn而不是handle,使用函数绑定后:

btn.addEventListener("click", bind(handler.handleClick, handler), true);//点击后返回Event handled

效果和下面闭包一样:

btn.addEventListener("click", function(event){
	handler.handleClick(event);
}, true);//点击后返回Event handled

也可以直接使用ES5提供的原生实现的bind()函数绑定:

btn.addEventListener("click", handler.handleClick.bind(handler), true);//点击后返回Event handled
  • 被绑定函数与普通函数相比需要更多的内存,同时也因为多重函数调用稍微慢一点。但在要将某个函数指针(比如例子中的handler.handleClick)以值的形式进行传递,并且这个函数只能在特定的环境中执行时,绑定函数便很有用了。

函数柯里化

函数柯里化用于创建已经设置好的一个或多个参数的函数。基本方法和函数绑定是一样的:使用一个闭包返回一个函数,两者的区别在于,函数柯里化返回的函数还需要设置一些传入的参数。函数柯里化的用途是指定函数的不变参数,使得多次调用函数的时候不用重复传入相同的参数,就像Java里面的static变量,不过js是通过闭包实现的。

  • 通用的柯里化函数(ES5的bind()函数同样可以实现柯里化,传入参数和curry()相同):
function curry(fn) {
    var args = Array.prototype.slice.call(arguments,1);//只固定了一个参数
    return function () {
        var innerArgs = Array.prototype.slice.call(arguments),
            finalArgs = args.concat(innerArgs);
        return fn.apply(null,finalArgs);
    };
}
//ES6
function curry(func,...oldval) {
    return (...newval) => func(...oldval, ...newval);
}
  • 使用:
//一个求和函数
function sum() {
    var finalSum = 0;
    for (var i = 0; i < arguments.length && typeof arguments[i] === "number"; i++){
        finalSum = finalSum + arguments[i];
    }
    console.log(finalSum);
}
var newSum = curry(sum, 3);
//var newSum = sum.bind(sum, 3);//效果一样
sum(1,2);//3
newSum(1,2);//6
  • 函数柯里化比较适合处理延迟执行函数的参数传递:

比如用js实现了某个动画:

function demo(divId) {
	var div = document.getElementById(divId),
	    left = div.offsetLeft;
	setTimeout(function () {
	    if (left <= 900){
	        div.style.setProperty("left",left+5+"px");
	        demo(divId);
	    }
	}, 50)
}

上面的延时函数每次执行都要传递一个相同的div的id,可以像下面这样简化:

demo = curry(demo, "demo");//"demo"是要固定的div的ID
//demo = demo.bind(demo, "demo")//ES5的bind()函数也支持柯里化
demo();
function demo(divId) {
   var div = document.getElementById(divId),
       left = div.offsetLeft;
   setTimeout(function () {
       if (left <= 900){
           div.style.setProperty("left",left+5+"px");
           demo();//再次调用函数无需再传入参数
       }
   }, 50)
}

这样处理的好处除了不需要每次向函数传递参数,减少性能开支外,还可以实现随时随地调用函数而不需要函数间连续传递参数。
比如上面的那个js实现的动画我需要在不同的函数体内重复调用,假如不用柯里化的话,我们可能需要这么实现:

function demo2(divID){
	......
	demo(divID);
	......
}
function demo3(divID){
	......
	demo(divID);
	......
}

而函数柯里化之后,我们只需要这样实现:

function demo2(){
	......
	demo();
	......
}
function demo3(){
	......
	demo();
	......
}

这样我们就不需要再在两个函数之间传递重复的参数。

  • 每个函数都会带来额外的开销,所以最好在必要的时候才使用函数柯里化和函数绑定,具体使用bind()还是curry()要根据是否需要object对象响应来决定的。

参考资料:《JavaScript高级程序设计》第三版

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值