「这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战」
JavaScript 中的 this 在严格模式和非严格模式下的表现方式不同,再加上修改 this 值的函数和箭头函数的引入,this 它糊了!!!
我们根据 this 使用的场景入手,来理一下。
哪些地方会用到 this?
想想 this 出现的地方,主要场景有 3 种,分别是:
-
全局中的 this
全局的 this 无论是否为严格模式都指的是 window 对象。
-
函数中的 this
函数中的 this 一般是指函数调用者,但是因为箭头函数和严格模式的存在,还有 bind 插一手就,this 有不同的取值方式。函数也是主战场,我们后面重点介绍。
-
类中的 this
类本质也是函数,只是类方法按严格模式设计。类中的 this 有一些特殊情况。
函数中的 this
ECMA规范中有说到,当函数初始化时会调用方法 FunctionInitialize (F, kind, ParameterList, Body, Scope)
, 参数 Kind 有3个可选值,分别是 Normal(普通函数),Method(方法),Arrow(箭头函数)。
每个函数都有一个叫 [[ThisMode]] 的私有属性,函数初始化时会根据不同的情况设置这个属性:
-
当 Kind 为 Arrow 时,[[ThisMode]] 会设置为 lexical,函数调用时从上下文中取 this 值;
-
当参数 F 是严格模式时,[[ThisMode]] 会设置为 strict,这时的 this 是函数调用者,调用者可能会是 undefined;
-
除了上面两种情况外,[[ThisMode]] 会设置为 global,这时的 this 是个 undefined,非严格模式下会指向全局对象 window;
普通函数-非严格模式
普通函数非严格模式下 [[ThisMode]] 为 global,所以 this 指向全局对象 window。如果把这个普通函数赋值给对象的属性再调用,则 this 指向调用的对象。
所以同一个函数,调用的方式不一样,this 的取值也不同(运行时绑定)。
function showThis() {
console.log(this);
}
var o = {
showThis: showThis
}
showThis(); // global
o.showThis(); // o
普通函数-严格模式
严格模式下的 [[ThisMode]] 为 strict,this 指向调用者,如果没有调用者则 this 为 undefined。
function showThis() {
"use strict"
console.log(this);
}
showThis(); // undefined
箭头函数
箭头函数的 [[ThisMode]] 为 lexical,从执行上下文中获取 this。
const showThis = () => {
console.log(this);
}
var o = {
showThis: showThis
}
showThis(); // global
o.showThis(); // global
方法
方法的实现方式和普通函数的严格模式一样,所以表现方式和普通函数严格模式一样。
class C {
showThis() {
console.log(this);
}
}
var o = new C();
var showThis = o.showThis;
showThis(); // undefined
o.showThis(); // o
类中的 this
- 类的构造函数中的 this 是一个常规对象,所有类的非静态方法都会添加到 this 的原型中;
class Foo {
constructor() {
const proto = Object.getPrototypeOf(this)
console.log(Object.getOwnPropertyNames(proto))
}
foo (){}
static foo2(){} //静态方法不会加到 this 的原型中
}
new Foo() //['constructor', 'foo']
- 在派生类中
- 如果没有调用 super() 方法之前不能使用 this,
super()
相当于执行了this = new Base()
; - 派生类如果有构造函数,必须调用
super()
或返回一个对象;
- 如果没有调用 super() 方法之前不能使用 this,
class Base {}
class Good extends Base {} //没有构造函数
class Good2 extends Base { //构造函数返回对象
constructor() {
return {}
}
}
class Good3 extends Base { //构造函数调用 super()
constructor() {
super()
}
}
class Bad extends Base { //有构造函数,既没有调用 super() 也没返回对象,new 的时候报错
constructor() {}
}
new Good()
new Good2()
new Good3()
new Bad() // Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
改变 this 的方法
改变 this 的方法有 3 个,call、apply、bind,我们下期再来聊一聊。
如有错误欢迎指出,欢迎一起讨论!