深入理解JavaScript系列(5):this

《深入理解 JavaScript 系列(2):执行上下文栈》中提到,当 JavaScript 代码执行代码时,就会创建一个执行上下文。

对于执行上下文,都有三个重要属性:

  • 变量对象 (Variable Object - VO)
  • 作用域链
  • this

我们来介绍一下 this。

this

什么是 this 呢?我们可以把 this 的指向当作执行时所指向的执行上下文。在不同的上下文中,this 的确定经常会发生问题。

this 是执行上下文中的一个属性。

1
2
3
4
globalContext = {
VO: {...},
this: globalContext.VO,
}

VO 就是上一节讲到的变量对象了。

this 与上下文中可执行代码的类型有直接关系,this 值在进入上下文时确定,并且在上下文运行期间永久不变

全局代码中的 this

全局代码中的 this 始终是全局对象本身。

1
2
3
4
5
6
7
8
this.a = 1;
console.log(a); // 1

b = 2;
console.log(this.b); // 2

var c = 3;
console.log(this.c); // 3

那么全局对象本身是什么呢?在浏览器中就是我们经常使用的 window

1
2
3
console.log(this); // window 对象
console.log(window); // window 对象
console.log(this === window); // true

函数中的 this

函数中的 this 是很奇特的,也是很有趣的。它的指向并不是固定的,而且可以通过手动进行修改。

我们在上面介绍到,this 是进入上下文时确定,所以在一个函数代码中,这个值在每一次完全不同。

注意:任何时候不能改变 this 的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let objBind = { value: 2 };
let obj = {
value: 1,
test: function () {
console.log(this === obj);
console.log(this.value);
// this = objBind; // 报错,任何时候不能改变 this 的值
// console.log(this.value);
},
};

// 我们来执行一下
obj.test(); // true, 1

objBind.test = obj.test;

objBind.test(); // false, 2

我们看到结果的结果是不同的。
发现,指向的结果是不是跟调用它的对象有关呢?事实就是这样,谁调用就指向谁

我们使用 obj 来调用 test 方法时,指向了 obj,打印 true,而我们将方法赋给 objBind 之后,objBind 来调用时,指向了 objBind

我们来看一下执行 test 方法时,test 上下文:

1
2
3
4
5
6
7
8
9
10
// obj 调用执行
testContext = {
AO: {...},
this: obj,
};
// objBind 调用执行
testContext = {
AO: {...},
this: objBind,
}

通过上下文,我们可以清楚的了解到 this 在运行中的指向。

我们来看一下还有一种方式,指向了全局对象。

1
2
let test = obj.test;
test(); // false, undefined

这时候执行 test 时的上下文:

1
2
3
4
testContext = {
AO: {...},
this: window
}

作为构造函数下的 this

1
2
3
4
5
6
7
8
9
10
11
function Constructor() {
console.log(this); // "a"对象下创建一个新属性
this.value = 1;
}

let x = new Constructor();
console.log(x.value); // 1

// {
// "value": 1
// }

函数调用时,手动设置 this

在这里就要使用到 Function.prototype.applyFunction.prototype.call 了。

在这里也介绍了 applycall 以及 bind,可以看我的这两篇文章 - 《深入理解 JavaScript 系列(8): call/apply 函数》《深入理解 JavaScript 系列(9): bind 函数》

1
2
3
4
5
6
7
8
9
10
11
let obj = {
value: 1,
};
function fn(a) {
console.log(this.value);
console.log(a);
}

fn(2); // undefined, 2
fn.call(obj, 20); // 1, 20
fn.apply(obj, [20]); // 1, 20

多言一句 - 箭头函数指向

箭头函数的 this 指向,不能按照上面的任何一种来。

箭头函数的 this 指向,是在定义时决定的,而不是在调用时决定

1
2
3
4
5
6
7
8
9
10
var value = 1;
let obj = {
value: 2,
test: () => {
console.log(this); // window 对象
console.log(this.value); // 1
},
};
// 按照上面的想法,打印出来的应该是 2,而实际打印的是 1
obj.test();
-------------------- 本文结束 感谢阅读 --------------------