前言
- this既不指向自身,也不指向函数的词法作用域。
- this的绑定不是在编写代码的时候绑定的,而是在它运行的时候进行的。
- this的绑定和函数的声明位置没有任何关系,只取决于函数的调用方式。
this的绑定规则
this有四种绑定规则:
- 默认绑定
- 隐式绑定
- 显示绑定
- new绑定
让我们先来解释一下这四个规则。
默认绑定
函数的独立调用是函数最常见的一种调用方式,即直接使用不带任何修饰的函数引用进行的调用,此时函数中的this会默认绑定到全局对象。(在非严格模式下,严格模式会绑定到undefined)。这就是默认绑定。
请看以下代码:
function foo () {
console.log(this.a);
}
var a = 2;
foo();//2
此时的foo()直接使用的是不加任何修饰的函数引用的调用,因此函数中的this默认绑定到全局对象。
如果是严格模式,就会报错。
function foo() {
"use strict"
console.log(this.a);
}
var a = 2;
foo(); // TypeError:this is undefined
隐式绑定
当函数的调用存在某个上下文对象时,函数中的this就会绑定在这个上下文对象上。即函数被某个对象当做引用属性被添加到对象中时。
请看下面的代码:
function foo () {
console.log(this.a);
}
var obj = {
a:2,
foo:foo,
}
obj.foo(); // 2
foo()的调用不在是独立函数调用的方式,它的前面加上了对象obj的引用,此时函数内的this就会绑定在这个对象上,this.a和obj.a是一样的。
注意
在函数调用的时候,前面如果出现对象引用链,函数中的this只会绑定到引用链的最后一个对象上。
代码示例:
function foo () {
console.log(this.a);
}
var obj1 = {
a:42,
foo:foo,
}
var obj2 = {
a:2,
obj1:obj1,
}
obj2.obj1.foo(); // 42
显示绑定
显示绑定并不像隐式绑定那样,必须在对象内部包含一个指向函数的引用属性,并通过这个属性间接的引用函数。显示绑定则不需要在对象内部包含函数引用,也能强制的调用函数。
JavaScript中可以使用函数的两个方法来实现这个——apply( )和call( )。
这两个方法是如何工作的呢?它们的第一个参数是一个对象,这个对象是给this准备的,接着在调用函数的时候,将this绑定到这个对象上,因为你可以指定要绑定的对象,所有该绑定称为显示绑定。
代码演示:
function foo () {
console.log(this.a);
}
var obj = {
a:2,
};
foo.apply( obj ); // 2
foo.call( obj ); // 2
这两个方法在this的绑定上是一样的,它们的区别是在其他的参数上,(当然我们不在这里讨论它俩的区别),foo()通过这两个方法可以将this绑定在所传入的对象上。
new绑定
在使用new来调用函数或者是构造函数的调用时,会发生以下四个步骤:
- 创建一个新对象
- 这个新对象会被执行[[Prototype]]链接
- 这个新对象会绑定函数的this。
- 如果函数没有返回其他的对象,那么new表达式中的函数调用就返回这个新对象。
在这里我们关心的是第1,3,4步,至于第2步,暂时先跳过。
考虑下面的代码:
function foo (a) {
this.a = a;
}
var bar = new foo(2);
console.log(bar.a); // 2
使用new 来调用foo(…)时,会构建一个新的对象,并将this绑定到这个新对象上,最后返回这个新对象。new是最后一种可以影响this绑定行为的方法,称之为new绑定。
绑定规则的优先级
现在我们已经了解了这四种绑定规则,但当一个函数运用多种绑定规则时该怎么办呢?因此我们现在来测试绑定规则的优先级的问题。
这就是绑定规则的优先级问题,而绑定规则的有限次序是:
new绑定 > 显示绑定 > 隐式绑定 > 默认绑定
总结
现在我们可以通过绑定规则的优先级来进行判断this的绑定对象:
函数如果是在new中调用,那么this就会绑定在新创建的对象的上。
var bar = new foo()
函数是否apply,call来调用,如果是的话,this就绑定到那个指定的对象上。
var bar = foo.apply(obj)
函数是否在某个上下午中调用,如果是的话,this就绑定到那个上下文对象上。
var bar = obj.foo()
如果以上都不是的话,那就是默认绑定,如果在严格模式下,this绑定的是undefined,如果是非严格模式下,this绑定的是全局对象。
var bar = foo()
this的绑定就是这样了。