在JavaScript中有很多改变this指向的方法,如call、apply,本文说的bind也是方法之一,与前两者不同的是,前两者改变this指向的时候会立即执行,而bind不会。
bind是函数原型Function.prototype上的一个方法,作用是改变this的指向并返回一个函数等待执行。
bind可以传入多个参数,第一个参数作为this的一个对象,后面的参数作为返回函数的形参。第一个参数不同,this指向也不同。
1、简单讲解bind函数
/*
*例一:讲解bind函数
* 简单来说:
* 1、函数obj.getValue调用bind的时候,返回一个新的函数newGetValue
* 2、新函数newGetValue和obj.getValue函数体一模一样
* 3、newGetValue函数被调用时的this是指向bind绑定时传入的第一个参数,
* 第一个参数为null、undefined时,this指向window
* 4、bind函数被调用时传递的参数,会在newGetValue被调用时被传递,并且排在实参的最前面
* 5、new newGetValue()时,会把newGetValue当成一个构造函数,this自动被忽略,参数依旧可以传。
*/
/*
var obj = {
value: "123",
getValue: function () {
console.log(this.value);
console.log([].slice.call(arguments));
}
};
obj.getValue();//123 //[]
obj.getValue(1, 2);//123 //[1,2]
var newGetValue1 = obj.getValue.bind(obj,'a','b');
newGetValue1(1);//123 //['a','b',1]
var newGetValue2 = obj.getValue.bind({}, 'a', 'b');
newGetValue2();//undefined //['a','b']
var value = 10;
var newGetValue3 = obj.getValue.bind(null);
newGetValue3(222);//10 //[222]
var newGetValue4 = obj.getValue.bind();
newGetValue4('a');//10 //['a']
new newGetValue1('f');//undefined //['a','b','f']
var newGetValue5 = obj.getValue.bind('3');
newGetValue4('a');//10 //['a']
2、原生js实现bind
首先,由上面可知,bind第一个参数不同,this指向不同,而且返回的是一个新的函数,当函数有返回值时还需要返回值。
Function.prototype.myBind = function(target){
var target = target || window;
return function(){
return this.apply(target);
};
};
其次,在myBind被调用的时候可以有多个形参,作为返回函数的参数,而新返回的函数也是可以传参的,由于参数个数可以不同,所以选择用arguments来确定参数,利用[].slice.call(arguments)将类数组切成数组。
Function.prototype.myBind = function(target){
var target = target || window;
var self = this;
//保存myBind调用时传递的参数
var args = [].slice.call(arguments,1);
//myBind返回值是一个函数
return function(){
//保存新返回的函数传递的参数
var _args = [].slice.call(arguments,0);
return self.apply(target,args.concat(_args));
};
};
到这里已经实现了bind方法指定this值和传递参数的功能。
最后,我们看一下构造函数的效果如何实现,忽略this的值,传参照常。要怎么知道调用的时候是通过构造函数调用的呢?
被当做构造函数调用的时候this指向一个新创建的对象,而被当做普通函数调用的时候,this指向的是window。那如何判断this指向这个新对象呢?别忘了,构造函数new一个新对象必然关系到原型链继承,那么bind函数不仅会复制被调用函数的函数体,原型链也要复制。这样我们就可以用instanceof检测新对象是否继承自被调用函数,如果是,那么就是被当做构造函数调用的。
代码如下:
Function.prototype.myBind = function(targe){
//如果调用myBind方法的不是一个函数,返回错误信息
if(typeof this !== 'function'){
throw new Error(this+'is not a function');
}
var target = target || window;
var self = this;
var args = [].slice.call(arguments,1);
var F = function(){
var _args = [].slice.call(arguments,0);
return self.apply(this instanceof self ? this:target,args.concat(_args));
};
F.prototype = Object.create(self.prototype);
return F;
};