JavaScript中this的解析

前言

  • 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来调用函数或者是构造函数的调用时,会发生以下四个步骤:

  1. 创建一个新对象
  2. 这个新对象会被执行[[Prototype]]链接
  3. 这个新对象会绑定函数的this。
  4. 如果函数没有返回其他的对象,那么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的绑定对象:

  1. 函数如果是在new中调用,那么this就会绑定在新创建的对象的上。

    var bar = new foo()

  2. 函数是否apply,call来调用,如果是的话,this就绑定到那个指定的对象上。

    var bar = foo.apply(obj)

  3. 函数是否在某个上下午中调用,如果是的话,this就绑定到那个上下文对象上。

    var bar = obj.foo()

  4. 如果以上都不是的话,那就是默认绑定,如果在严格模式下,this绑定的是undefined,如果是非严格模式下,this绑定的是全局对象。

    var bar = foo()

this的绑定就是这样了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章