面试题 | this指向

废话不多说!直接看题!

题目描述

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
28
29
var n = 123;

function fn() { // ----------------(1)
console.log(this.n);
}


let obj = {
n: 456,
fn1: function() { // ----------------(2)
console.log(this.n);
},
fn2: function() { // ----------------(3)
fn();
},

fn3: fn, // ----------------(4)
fn4: () => { // ----------------(5)
console.log(this.n);
},
}

var bar = obj.fn1; // ----------------(6)

obj.fn1(); // 456
obj.fn2(); // 123
obj.fn3(); // 456
obj.fn4(); // 123
bar(); // 123

考察知识点

this指向问题【常考!必考!头疼!】

这里仅对非严格模式下this指向问题进行讨论,严格模式不做讨论

  • 一般来说,谁调用this就指向谁
  • 在非严格模式下,没有明确的调用者this指向window
  • 箭头函数,this指向定义箭头函数的上下文

解析

依次分析结果:

(1)处直接在全局变量window里定义了一个函数,我们知道,对于所有JavaScript全局对象、函数以及用var声明的变量均自动成为window对象的成员,如果直接调用fn(),this指向window(可以理解为是调用了挂载在window上的全局方法fn)

1
2
3
4
5
var n = 123;
function fn() { // ----------------(1)
console.log(this.n);
console.log(this); // window
}

(2)处在obj对象上定义了fn1方法,如果通过obj.fn1()调用,this指向调用这个方法的上下文对象,即obj(可以理解为此时fn1是挂载在obj上的)

1
2
3
4
5
6
7
8
let obj = {
n: 456,
fn1: function() { // ----------------(2)
console.log(this.n);
console.log(this); // obj
},
...
}

(3)处特殊的点在于:在obj对象上定义了fn2方法,但是这个方法里面嵌套的是调用在(1)处定义的fn函数,根据(2)的分析结果,(3-1)处的this指向为obj,但是!这里调用的是fn(),fn()内部的this指向并没有变化【回顾一下,改变this指向的方法是call apply apply】,因此(3-2)处fn的this指向仍为window

1
2
3
4
5
6
7
let obj = {
...
fn2: function() { // ----------------(3)
console.log(this); //(3-1) obj
fn(); //(3-2) window
},
}

(4)处特殊的点在于:在obj对象上定义了fn3方法,但是这个方法是通过fn赋值得到的,如果你深刻理解函数名是指向函数的指针,就非常容易理解了,fn3处的代码可作改写,更进一步理解!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function fn() {
console.log('this指向', this);
}
let obj = {
...
fn3: fn, // ----------------(4)
/*
可以改写为
fu3: function() {
console.log('this指向', this);
}
*/
}

obj.fn3(); // this指向 obj {...}

(5)处特殊的点在于:fn4是一个箭头函数,箭头函数中的this指向的是定义箭头函数的上下文,箭头函数的作用域在函数fn4内部,fn4所在作用域为最外层的js环境,因为其没有其他函数包裹,最外层的js环境指向的对象是window对象,所以this指向window对象

1
2
3
4
5
let obj = {
fn4: () => { // ----------------(5)
console.log(this.n);
},
}

(6)处是在全局定义了两个变量bar,基本思路和(4)处有异曲同工之妙,不多说了

1
var bar = obj.fn1;                             // ----------------(6) 

拓展

下面打印的结果为:

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
let n = 123;

function fn() {
console.log(this.n);
}


let obj = {
n: 456,
fn1: function() {
console.log(this.n);
},
fn2: function() {
fn();
},

fn3: fn,
fn4: () => {
console.log(this.n);
},
}

obj.fn1(); // 456
obj.fn2(); // undefined
obj.fn3(); // 456
obj.fn4(); // undefined

因为,var 命令和 function 命令声明的全局变量,依旧是顶层对象的属性,但 let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性

知识点

函数 | 深刻理解函数名是指向函数的指针

变量 | var let const区别及应用

函数 | this指向