ES5中bind函数的特性: 权威参考MDN
语法:fun.bind(thisArg[,arg1[,arg2[,...]]])
参数:
thisArg
:当绑定函数被调用时,该参数会作为原函数运行时的this指向。当使用new操作符调用时绑定函数时,该参数无效。
arg1,arg2,...
:当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。
返回值
:返回由指定的this值和初始化改造的原函数拷贝
-
不同于call和apply只是单纯的设置this的值后传参,它还会将所有bind()方法中的实参(第一个参数之后的参数)与this一起绑定。
eg1:var sum = function(x,y){ return x+y; } var succ = sum.bind(null,1) //让this指向null,其后的实参也会作为实参传入被绑定的函数sum succ(2); //输出3,可以看到1绑定了sum函数中的x
eg2:
var copy = function(){ var args = Array.prototype.slice.call(arguments); console.log(args.join()); } copy("hi","friends") //输出 hi,friends var after = copy.bind(null,"hi","welcome"); after("dear","friend"); //输出 hi,welcome,dear,friends
-
bind()方法所返回的函数的length(形参数量)等于原函数的形参量减去传入bind()方法中的实参数量(除去第一个表示左右域的参数),因为传入到bind中的实参都会绑定到原函数的形参
eg:function func(a,b,c,d,e){...} //func.length = 5 var after = func.bind(null,1,2); //这里传入两个实参绑定到了func函数的a,b console.log(after.length) //输出为3
注意:这里bind()方法的第一个参数为null,是因为func()函数内部没有任何与上下文相关的代码,所以不需要传递上下文对象
当bind()所返回的函数用作构造函数时,传入bind()的this将被忽略,实参会全部传入原函数
eg:
var person=function(y){
this.name="Amy",
this.printName=function(){console.log(this.name+" "+y)}
}
var anotherPerson={
name:"Bob"
}
var Test = person.bind(anotherPerson,"Jack");
var test = new Test(); //或者var test = new(Test);
console.log(test.name) //输出 "Amy"
console.log(test.printName()) // 输出"Amy Jack"
```
可以看到,Test为一个绑定函数,传入了anotherPerson作为上下文对象,传入了“Jack”作为预设参数,使用new调用Test时,传入的this完全被忽略,但是预设的实参能够被传入
4. bind()方法返回的是一个带有固定作用域的新函数,以后不管在哪儿调用这个新函数,都不用担心作用域的问题,**即使是之后在使用call函数作用于这个新函数,也不会改变函数执行的上下文对象**
eg:
```
var name = "Global";
var student = {
name:"John"
}
var person = {
name:"Amy",
sayName:function(){console.log(this.name)}
}
var one={
name:"Bob"
}
var sayName = person.sayName;
var bindSayName = person.sayName.bind(student);
console.log(person.sayName()) // "Amy"
console.log(sayName()) // "Global"
console.log(bindSayName()) //"John"
console.log(bindSayName.call(one)) //"John"
```
此时bindSayName这个函数已经绑定到了student这个上下文,接下来用call调用它也不会改变上下文对象
5. 配合setTimeout和setInterval函数使用
一般在函数或类的实例中使用setTimeout和setInterval函数时,this会默认指向window对象,所以这时需要显示的绑定this对象
eg:
```
var name = "Global";
var person = {
name:"Amy",
sayName:function(){console.log(this.name)},
printName:function(){
setTimeout(this.sayName,1000);
}
}
console.log(person.printName()) //"Global"
```
出现上述问题的原因是setTimeout把person的printName函数放到全局函数作用域中执行去了,所以打印的是全局作用域中的name变量,故而需要手动的在setTimeout函数中绑定this对象,改进如下:
```
var name = "Global";
var person = {
name:"Amy",
sayName:function(){console.log(this.name)},
printName:function(){
setTimeout(this.sayName.bind(this),1000);
}
}
console.log(person.printName()) //"Amy"
```
6. 兼容性问题:
bind()是在ES5中加入的新特性,所以无法在所用浏览器上运行,例如IE6、7、8都不支持,因此需要手动的实现bind()函数
##### 贴出polyfill官方文档上的实现:
```
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1), //(1)
fToBind = this, //(3)
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP? this: oThis ||this,
aArgs.concat(Array.prototype.slice.call(arguments))); //(2)
};
fNOP.prototype = this.prototype; //(4)
fBound.prototype = new fNOP(); //(5)
return fBound;
};
}
```
理解:
**以1中的eg2举个例子:**
```
var copy = function(){
var args = Array.prototype.slice.call(arguments);
console.log(args.join());
}
copy("hi","friends") //输出 hi,friends
var after = copy.bind(null,"hi","welcome");
after("dear","friend"); //输出 hi,welcome,dear,friends
```
(1) `aArgs`是定义bind函数时传入的除第一个参数oThis之外的预设参数,在eg2中为["hi","welcome"]
(2) 这里的`arguments`是bind绑定后返回的函数`after`调用时传入的参数,其是类数组对象,调用Array.prototype.slice.call(arguments)后转化为数组,在eg2中为["dear","friends"]
(3) 这里的fToBind就是被绑定的原来的函数"copy"
(4) 本来`fBound.prototype = fToBind.prototype`就可以让新函数`after`继承原来函数`copy`的所有属性,但这样新函数和原来被绑定的函数指向同一个地方,对新函数的任何修改会影响到原来被绑定的那个函数,不符合要求,所以引入fNOP这个函数作为中转,让bind绑定后返回的函数fBound指向继承了被绑定的函数fToBind的所有属性的一个新的函数fNOP,此时之后对fBound函数做的修改只会影响fNOP这个函数,而与fToBind没有任何关系