Skip to content

类运行时中的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 调用。