this 指向

this关键字的值指向的其实是当前执行上下文Execution context)。

而执行上下文分为三种——全局执行上下文(Global execution context)、函数执行上下文(Functional execution context)和eval执行上下文(Eval),每种上下文中都对应着一种this。

由于eval使用极少,下面只讲解全局执行上下文函数执行上下文中的this

全局执行上下文中的this

直接在控制台执行console.log(this),打印出来的就是全局执行上下文中的this,最终输出的是window对象。

由此,我们可以得出一个结论——全局执行上下文中的this指向window对象。

函数执行上下文中的this

函数执行上下文中的this指向取决于函数调用的方式,如果是作为普通函数执行,则this指向window对象。如果作为对象属性调用则指向对象本身。

执行以下代码:

function foo(){
  console.log(this)
}
foo() //window
1
2
3
4

最终输出的还是window对象。那么再看下面一段代码:

const obj = {
	foo () {
		console.log(this);
	}
};

obj.foo(); // obj
1
2
3
4
5
6
7

此时this指向的是obj对象而不是window。那么为什么同样都是函数调用,作为对象属性调用就会指向对象本身呢?

虽然上面代码对象是以字面量形式声明,但我们知道其实它相当于使用new方法创建了Object实例,而执行new指令会进行以下操作:

  1. 创建一个空的简单JavaScript对象(即**{}**);
  2. 为步骤1新创建的对象添加属性**__proto__**,将该属性链接至构造函数的原型对象 ;
  3. 将步骤1新创建的对象作为**this**的上下文 ;
  4. 如果该函数没有返回对象,则返回**this**。

可以通过以下代码来理解:

function excuteNew (fn, ...args) {
	let obj = Object.create(fn.prototype);
	let res = fn.applay(obj, args);
	return typeof res === 'object' ? res : obj;
}
1
2
3
4
5

可以看到其实是因为new内部修改了this的指向,所以才能指向对象本身。

看到这里就基本了解了函数中this的指向问题,结合上面分析下以下代码:

var myObj = {
  name : "myobj", 
  showThis: function(){
    console.log(this)
    function bar(){console.log(this)}
    bar()
  }
}
myObj.showThis()
1
2
3
4
5
6
7
8
9

首先执行myObj.showThis(),此时showThis作为对象属性调用,所以第一个this应该打印的myObj。执行到内部bar函数时,此时bar虽然在myObj.showThis中执行,但它依旧是作为普通函数被调用,所以第二个打印的是window

箭头函数中的this指向

箭头函数与普通函数不太一样,因为箭头函数并没有自己的this,它内部的this就是其外层代码块中的this,我们来分析下以下代码:

var a = 'global';
var obj={
    a: 'inner',
		bar () {
			var bar = () => {
				console.log(this.a);
			};
			return bar;
		},
    foo () {
        console.log(this.a);
        var foo = () => {
            console.log(this.a);
        };
        foo();
    }
};

obj.foo(); // inner, inner
var foo = obj.foo;
foo(); // global, global

obj.bar()(); // inner
var bar = obj.bar();
bar(); // inner
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

执行obj.foo时,内部this指向自身,而内部foo中的this就是外部的this,所以此时内部foothis也指向obj。而将obj.foo赋值给变量foo再执行foo,此时foo作为普通函数执行,内部this指向window,所以此时内部foo中的this也指向window。这里要注意,this的指向是在代码执行时才确定的

上面的解释可能有点难懂,其实通过babel转换成ES5语法就一目了然了,转换后的代码如下:

var a = 'global';
var obj = {
  a: 'inner',

  bar() {
    var _this = this;

    var bar = function () {
      console.log(_this.a);
    };

    return bar;
  },

  foo() {
    var _this2 = this;

    console.log(this.a);

    var foo = function () {
      console.log(_this2.a);
    };

    foo();
  }

};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

其实就相当于使用了一个变量保存了父级函数的this

总结

  1. 当函数作为对象的方法调用时,函数中的 this 就是该对象;
  2. 当函数被正常调用时,在严格模式下,this 值是 undefined,非严格模式下 this 指向的是全局对象 window
  3. 箭头函数的this指向外层代码块的this