this关键字
与其他语言相比,函数的 this
关键字在 JavaScript 中的表现略有不同,此外,在严格模式和非严格模式之间也会有一些差别。它是当前执行上下文(global、function 或 eval)的一个属性,在非严格模式下,总是指向一个对象,在严格模式下可以是任意值。
全局上下文
无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this
都指向全局对象。
Note: 你可以使用 globalThis 获取全局对象,无论你的代码是否在当前上下文运行。
// 在浏览器中, window 对象同时也是全局对象:
console.log(this === window); // true
a = 37;
console.log(window.a); // 37
this.b = "MDN";
console.log(window.b); // "MDN"
console.log(b); // "MDN"
函数上下文
在函数内部,this
的值取决于函数被调用的方式。
因为下面的代码不在严格模式下,且 this
的值不是由该调用设置的,所以 this
的值默认指向全局对象,浏览器中就是window。
//在浏览器中:
console.log(f1() === window); // true
//在Node中:
console.log(f1() === globalThis); // true
然而在严格模式下,如果进入执行环境时没有设置 this
的值,this
会保持为 undefined
,如下:
function f2() {
"use strict"; // 这里是严格模式
return this;
}
console.log(f2() === undefined); // true
一个JavaScript类示例:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
study() {
console.log(this);
}
}
const p = new Person("Tom", 18);
p.study(); // Person {name: 'Tom', age: 18}
const x = p.study;
x(); // undefined
灵魂三连问:
1、study方法放在了哪里?—— Person类的原型对象上(Person.prototype)
2、谁调用了study方法?—— 供Person类的实例使用(这里指p)
3、study方法中this是谁?—— Person类实例
控制台输出如图:
接下来做这么一个操作:
const x = p.study;
x(); // undefined
将右边实例对象p的study方法传给左边常量x,然后调用x(),控制台输出undefined。小朋友,你是否有很多问号?为什么 通过赋值的方式,函数内部this的指向会发生改变?那解释这个问题之前,首先你需要明白一点,这里的常量x只是指向p中study方法,并不是调用。换句话说因为study是被直接调用的,而不是作为对象的属性或方法调用的,而能够使函数上下文this指向undefined的情况,上面提到了,就是在严格模式下,问题又来了,我们写的示例代码中没有在严格模式下编写啊,这么阴间的嘛(<_<),哪位道友鬼使神差地加上去的?
这是因为根据 JavaScript 的语法规则,所有在类中定义的方法都默认开启局部严格模式。
对比下面示例中的两个函数内部this指向:
function func1() {
console.log(this);
}
function func2() {
"use strict";
console.log(this);
}
func1(); // window 对象信息
func2(); // undefined
再来完整回答上边的问题,因为常量x指向的方法 study()
是在 Person 类中定义的,其默认开启了局部严格模式。当 study()
被调用时,调用方为 window
对象,这样 this
就变成了 undefined
。
改变函数上下文this指向的方法有以下两种:ES5 引入了 bind 方法来设置函数的 this
值,而不用考虑函数如何被调用的。ES2015 引入了箭头函数,箭头函数不提供自身的 this 绑定(this
的值将保持为闭合词法上下文的值)。
我们只需要在Person类的构造函数内写上这么一行代码即可:
this.study = this.study.bind(this);
通过调用bind方法,给Person实例上的study方法绑定了this值,因此不管是通过对象的属性或方法调用,还是直接调用,this的指向都是唯一的,即Person的实例。关于bind的其他使用这里就不深入细讲了,感兴趣的小伙伴可以自行研究。