一个简单的 JavaScript 类示例
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
speak() {
console.log(this)
}
}
const p = new Person('Callback', 27)
p.speak();
执行上面的示例可以看到控制台输出如下图
接下来做一个操作:
const f = p.speak
f() // 输出 undefined
这里将常量 f
指向 speak()
方法,注意这里是「指向」,并不是「调用」。之后调用 f()
可以观察到控制台输出为 undefined
。那么问题来了,为什么这么赋值之后,函数内部的 this
不再是 Person 的实例 p
而变成了 undefined
?
实际上,方法内部的 this
永远指向的是调用这个方法的对象,通过 Person 的实例 p
调用 speak()
方法,那么 this
就指向 p
。后面将 speak
赋值给常量 f
后, f()
表示的就是通过全局的 window
对象来调用 f()
方法。按理说,f()
方法被调用时应该输出 window
对象的信息,但这里输出的却是 undefined
。这是因为根据 JavaScript 的语法规则,所有在类中定义的方法都默认开启局部严格模式。在严格模式下,所有指向 window
对象的 this
,都全部变更为 undefined
。看下面的示例:
function demo() {
console.log(this)
}
function demo2() {
'use strict'
console.log(this)
}
demo() // 输出 window 对象信息
demo2() // 输出 undefined
回到最初的问题,之所以调用 f()
方法后输出 undefined
,是因为 f
指向的方法 speak()
是在 Person 类中定义的,其默认开启了局部严格模式。当 f()
被调用时,调用方为 window
对象,这样 this
就变成了 undefined
。
其实这一点和 Java 中对方法的处理是类似的。每一个实例对象都持有对方法的引用而不是保存一份方法,方法会自动在参数列表的最前面添加一个 this
参数,这就是我们之所以能够在 Java 方法里使用 this
关键字的原因,而这个 this
指向的就是当前在调用这个方法的对象。区别在于,Java 里所有的方法都不能脱离定义它的类,不可以像 JavaScript 里面把类的某个方法赋值给一个全局变量或常量。