JS中的this指向

在JS代码中,对于this我们只需要关注它的指向,确保返回的是一个正确的值。this的指向由下面这几点决定:

  1. 对象调用某函数,其中的this就指向该对象
  2. 通过apply、bind、call来显示指定函数的this
  3. new一个函数,函数中的this指向返回的实例化对象
  4. 箭头函数的this指向父级作用域的this

    TS中的this指向和this类型

    但是在TS代码中,我们要使用this,还需要确认this的类型
    下面我们看一个代码
    1. const option = {
    2. x: 1,
    3. y: 2,
    4. methods: {
    5. getOption: function ():number[] {
    6. return [this.x, this.y];
    7. },
    8. },
    9. };
    你知道getOption中的this指向谁吗?答案应该是不知道,因为非箭头函数的this指向,只在调用时才能确定,静态编译期是确定不了的。
    而在TS中,不仅要关心this的指向,还要关心this的类型。如果在编辑器中写上面的代码,编译器会报类型警告:
    image.png

    图中所示,TS将this推断为含有getOption属性的对象,这不是我们想要的

我们想要的是,this代表option对象,通过this可以访问x、y。这里我们需要显示地定义this的类型。

应该怎么做呢

用interface定义this的类型

可以在形参的位置定义this的类型

  1. interface I_Option {
  2. x: number;
  3. y: number;
  4. getOption: () => [number, number];
  5. }
  6. const option = {
  7. x: 1,
  8. y: 2,
  9. methods: {
  10. getOption: function (this: I_Option) {
  11. return [this.x, this.y];
  12. },
  13. },
  14. };

这里先定义了一个接口I_Option,其中的结构和option相同。之后,在getOption的形参中,对this的类型做了定义。现在不会报类型警告了

当然这个只是类型的推断,this的真正指向还要看代码运行阶段。

上面是解决定义this类型定义的一个方法,还有一种方法,我们可以用TS内置函数ThisType<Type>来实现

用ThisType实现对this类型的定义

定义一个通用的对象类型

  1. const obj = {
  2. data: {},
  3. methods:{}
  4. }

对象中的属性,无非就两种:数据,方法。所以上面的结构中,把数据全部放在了data里面,把方法全部放在了methods里面。

定义这样一个通用类型的对象,无非就是之后的接口类型定义能够方便一点

定义通用对象的接口类型

  1. interface I_configObject<D,M>{
  2. data: D;
  3. methods: M
  4. }

接口接收两个泛型,第一个泛型作为data的类型,第二个泛型作为methods的类型

定义一个创建对象的函数

  1. function createObj<D, M>(configObj: I_configObject<D, M>) {
  2. return {
  3. ...configObj.data,
  4. methods: ...configObj.methods,
  5. };
  6. }

解释:

  1. 函数接收具有通用结构的configObj
  2. createObj也接受两个泛型,这两个泛型与data和methods的类型一致
  3. 你知道return中,为啥要这么做吗

    使用createObj函数,看看效果

    1. const option2 = createObj({
    2. data: {
    3. x: 1,
    4. y: 2,
    5. },
    6. methods: {
    7. getOption: function ():number[] {
    8. return [this.x, this.y];
    9. },
    10. },
    11. });
    然后我们将符合通用类型的结构放进createObj函数中,就可以得到我们想要的option啦
    作者:额,心细的你是不是发现了什么?
    读者:对,没有用ThisType啊,这连影子都没有见到。
    作者:哦哦哦,对不起,立马加上

    如果你在编辑器中敲了上面的代码,发现还是会在this的地方报类型警告

  1. interface I_configObject<D,M>{
  2. data: D;
  3. // 表示methods中的this是D & M类型的,
  4. // 所以在methods中,可以访问D中的属性(x,y)
  5. methods: M & ThisType<D & M>
  6. }

现在可以了,不会报类型警告了
下图中,可以看到TS推断出的this类型:
image.png

这正是我们想要的

其次,也可以放在 createObj的形参中

  1. //表示configObj中的this指向是D & M类型的
  2. function createObj<D, M>(configObj: I_configObject<D, M> & ThisType<D & M>) {
  3. return {
  4. ...configObj.data,
  5. methods: configObj.methods,
  6. };
  7. }

效果是一样的

ThisType仅支持在对象字面量的上下文中使用,在其他地方使用作用等同于空接口

总结:

  1. JS中的this指向
  2. TS中,不仅关心this指向,还要关注this的类型
  3. 使用interface中,在形参中对this进行类型注释
  4. 使用ThisType对this进行类型注释
  5. 用ThisType做类型注释真是磨人