这是我参与8月更文挑战的第十八天,活动详情查看:8月更文挑战
绑定规则
我们来看看在函数的执行过程中调用位置如何决定this的绑定对象。我们需要先找到调用位置,然后判断需要应用四条规则中的哪一条。首先让我们来看看这四条规则
显式绑定
在分析隐式绑定时,我们必须在一个对象内部包含一个指向函数的属性, 并通过这个属性间接引用函数,从而间接的把this绑定到这个对象上面。那如果我们不想在对象内部包含函数引用,而想在某个对象上强制调用函数,应该怎么操作呢?
JavaScript中的“所有”函数都偶有一些有用的特性,可以用来解决这个问题。想必有些同学已经猜到了,就是函数的call()和apply()方法。这两个方法的第一个参数是一个对象,是给this准备的,接着在调用函数时将其绑定到this。因为这种操作可以直接指定this的绑定对象,所以称之为显式绑定。
function foo(){
console.log(this.a);
}
var obj={
a:2
};
foo.call(obj); //2
复制代码
通过foo.call(),我们在调用foo时强制把它的this绑定到了obj上面。这里值得一提的是,如果传入了一个原始值的参数(字符串、布尔、数字)来当做this的绑定对象,这个原始值会被转换成它的对象形式(即new String()、new Boolean()、new Number())。这个特性被称为装箱。可惜的是,显式绑定无法解决之前的绑定丢失问题。
硬绑定
但是显式绑定的一个变种可以解决这个问题。
function foo(){
console.log(this.a);
}
var obj = {
a:2
};
var bar = function(){
foo,call(obj);
};
bar(); //2
setTimeout(bar,100) ; //2
bar.this(window); //2
复制代码
上面的例子在函数bar()内部手动调用了foo.call(obj),因此强制把foo的this绑定到了obj。无论后面怎么调用函数bar,总会手动在obj上调用foo。这是一种显式的强制绑定,因此被称之为硬绑定。
因为硬绑定是一种非常常见的模式,所以早在ES5就提供了内置的方法Function.prototype.bind:
function foo(something){
console.log(this.a, something);
returen this.a + something;
}
var obg = {
a:2
};
var bar = foo.bind(obj);
var b = bar(3); // 2 3
console.log(b); //5
复制代码
bind()会返回一个硬编码的新函数,它会把指定的参数设置为this的上下文并调用原始函数。
API调用的上下文
第三方库的许多函数,以及JavaScript语言和宿主环境中许多新的内置函数,都提供了一个可选的参数,通常被称为上下文,作用和bind()一样,确保回调函数使用指定的this。
function foo(el){
console.log(el,this.id);
}
var obj = {
id :"awesome"
};
//调用foo()时把this绑定到obj
[1,2,3].forEach(foo,obj); //1 awesome 2 awesome 3 awesome
复制代码
这些函数实际上就是通过call()或者apply()实现了显式绑定。