# 1.前言

在 JavaScript 中,this 的指向一直是大多数初学者的易错点,总是搞不清楚 this 到底指向谁,而在求职面试中,this 的指向问题往往又是高频考点。本篇博文就来总结一下在 JavaScript 中不同情况下 this 到底指向谁。

# 2.热身一下

首先,我们先来看看下面的代码,请问,下面这段代码运行后会在控制台输出什么?如果你能马上回答出程序输出的结果,那么你已经很清楚 this 的指向了,不用再往下看了。

var bar = 2;
var obj = {
  bar: 1,
  foo: function() {
    console.log(this.bar);
  },
  boo: (function() {
    console.log(this.bar);
  })(),
};

var foo = obj.foo;

obj.foo();
foo();

// 输出
// 2
// 1
// 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

通过分析代码,我们可以知道,第一个 2 是在第 8 行代码处输出的,第二个 1 是在第 14 行代码处输出的,第三个 2 是在第 15 行代码处输出的。这样的结果显然是由于不同地方的 this 指向不同,导致此 this.bar 非彼 this.bar。那么到底该怎么区分不同情况下的 this 到底指向谁呢?下面我们就来总结下在 JavaScript 中几种不同情况下的 this 指向问题,只要记住了以下五种情况,保准你以后遇到这样的面试题不再懵圈,哈哈哈哈哈。

# 3.情况一:自执行函数

//自执行函数
boo: (function() {
  console.log(this.bar); //输出2
})();
1
2
3
4

在上面的热身代码中,boo 函数是一个自执行函数,也就是说当浏览器运行这段代码时,会先自动执行 boo 函数。切记:自执行函数里面的 this 指向 window 全局对象。既然 this 指向了 window,那么 this.bar 即就是 window.bar,所以在该行代码处输出 2.

# 4.情况二:函数调用模式

foo: function () {
  console.log(this.bar)    //输出2
},
foo() // 函数调用模式
1
2
3
4

在形如热身代码中第 15 行这样的单纯的函数调用时,那么切记:在单纯的函数调用模式中,被调用函数内部的 this 指向 window 全局对象。所以 this.bar 即就是 window.bar,在该行代码处输出 2.

# 5.情况三:方法调用模式

var obj = {
  bar: 1,
  foo: function() {
    console.log(this.bar); //输出1
  },
};
obj.foo(); // 方法调用模式
1
2
3
4
5
6
7

在形如 obj.foo()这种,对象.方法这种模式我们称为方法调用模式,在这种模式中,this 指向调用这个方法的对象。在热身代码中,由于 foo 函数是被 obj.foo()这种方式调用的,那么 foo 函数内的 this 就指向了 obj,因此 this.bar 即就是 obj.bar,所以在该行代码处输出 1。

Tips:关于情况二和情况三,我们可以简单粗暴的这么记:函数执行的时候,看函数名前面是否有".",有的话"."前面是谁 this 就指向谁,没有的话 this 就指向 window 全局对象

# 6.情况四:构造函数调用模式

这种情况最容易理解,在使用构造函数实例化对象时,构造函数中的 this 指向实例化出来的新对象。

function Person(name) {
  this.name = name;
  console.log(this); //输出xiaoming
}

xiaoming = new Person("xiaoming");
1
2
3
4
5
6

# 7.情况五:apply/call 改变 this 指向

apply 和 call 这两个方法,可以修改函数调用上下文,也就是 this 的指向。call 和 apply 的区别如下:

  • apply        函数.apply(对象, 函数需要参数列表,是一个数组)

  • call     函数.call(对象,函数所需要的参数 1,参数 2,参数 3...参数 n)

    1.第一个参数都是要把 this 修改指向的对象

    2.当函数需要参数的时候,那么 apply 是用数组进行参数的传递,而 call 是使用单个的参数进行传递

    3.apply 和 call 方法第一个传入参数是 null 的时候都表示为函数调用模式,也就是将 this 指向 window

对于这种情况,我们只需看函数的第一个参数是谁 this 就指向谁,如果是 null,则指向 window 全局对象。