Appearance
类运行时中的this
重要的是要记住,TypeScript并没有改变JavaScript的运行时行为,而JavaScript的运行时行为偶尔很奇特。
比如,JavaScript对这一点的处理确实是不寻常的:
typescript
class MyClass {
name = "MyClass";
getName() {
return this.name;
}
}
const c = new MyClass();
const obj = {
name: "obj",
getName: c.getName,
};
// 输出 "obj", 而不是 "MyClass"
console.log(obj.getName());
长话短说,默认情况下,函数内this的值取决于函数的调用方式。在这个例子中,因为函数是通过obj引用调用的,所以它的this值是obj而不是类实例。
这很少是你希望发生的事情! TypeScript提供了一些方法来减轻或防止这种错误。
1、箭头函数
如果你有一个经常会被调用的函数,失去了它的 this 上下文,那么使用一个箭头函数而不是方法定义是有意义的。
typescript
class MyClass {
name = "MyClass";
getName = () => {
return this.name;
};
}
const c = new MyClass();
const g = c.getName;
// 输出 "MyClass"
console.log(g());
这有一些权衡:
- this 值保证在运行时是正确的,即使是没有经过TypeScript检查的代码也是如此。
- 这将使用更多的内存,因为每个类实例将有它自己的副本,每个函数都是这样定义的。
- 你不能在派生类中使用 super.getName ,因为在原型链中没有入口可以获取基类方法。
2、 this 参数
在方法或函数定义中,一个名为 this 的初始参数在TypeScript中具有特殊的意义。这些参数在编译过程中会被删除。
typescript
// 带有 "this" 参数的 TypeScript 输入
function fn(this: SomeType, x: number) {
/* ... */
}
typescript
// 编译后的JavaScript结果
function fn(x) {
/* ... */
}
TypeScript检查调用带有 this 参数的函数,是否在正确的上下文中进行。我们可以不使用箭头函数,而是在方法定义中添加一个 this 参数,以静态地确保方法被正确调用。
typescript
class MyClass {
name = "MyClass";
getName(this: MyClass) {
return this.name;
}
}
const c = new MyClass();
// 正确
c.getName();
// 错误
const g = c.getName;
console.log(g());
这种方法做出了与箭头函数方法相反的取舍:
- JavaScript调用者仍然可能在不知不觉中错误地使用类方法
- 每个类定义只有一个函数被分配,而不是每个类实例一个函数
- 基类方法定义仍然可以通过 super 调用。