23

类方法,作为函数的类属性和作为箭头函数的类属性有什么区别?this关键字在方法的不同变体中的行为是否不同?

class Greeter {
  constructor() {
    this.greet();
    this.greet2();
    this.greet3();
  }

  greet() {
    console.log('greet1', this);
  }

  greet2 = () => {
    console.log('greet2', this);
  }

  greet3 = function() {
    console.log('greet3', this);
  }
}
      
let bla = new Greeter();

这是从 TypeScript 转译时生成的 JavaScript。

var Greeter = /** @class */ (function () {
function Greeter() {
    var _this = this;
    this.greet2 = function () {
        console.log('greet2', _this);
    };
    this.greet3 = function () {
        console.log('greet3', this);
    };
    this.greet();
    this.greet2();
    this.greet3();
}
Greeter.prototype.greet = function () {
    console.log('greet1', this);
};
return Greeter;
}());
var bla = new Greeter();

我的 TypeScript 版本是 3.4.5。

4

2 回答 2

24

所有 3 个版本之间都存在差异。这种差异体现在 3 个方面:

  1. this在运行时
  2. 分配功能的位置
  3. this打字稿中的类型是什么。

让我们从它们工作的地方开始。考虑这个类,有一个类字段:

class Greeter {
  constructor(private x: string) {
  }
  greet() {
    console.log('greet1', this.x);
  }

  greet2 = () => {
    console.log('greet2', this.x);
  }

  greet3 = function () {    
    // this is typed as any 
    console.log('greet3', this.x);
  }
}

let bla = new Greeter(" me");

使用此类,所有 3 个函数调用都将按预期打印:'greet* me'当调用时bla

bla.greet()
bla.greet2()
bla.greet3()

运行时这是谁

this箭头函数从声明上下文中捕获,因此始终保证thisingreet2是创建此函数的类实例。其他版本(方法和函数)不做这样的保证。

所以在这段代码中,并非所有 3 都打印相同的文本:

function call(fn: () => void) {
  fn();
}

call(bla.greet) // greet1 undefined 
call(bla.greet2) //greet2 me
call(bla.greet3) // greet3 undefined

在将函数作为事件处理程序传递给另一个组件时,这一点尤其重要。

分配功能的位置

类方法(例如greet)在原型上分配,字段初始化(例如greet2greet3)在构造函数中分配。这意味着greet2并且greet3将具有更大的内存占用,因为它们每次Greeter实例化时都需要分配一个新的闭包。

打字稿中 this 的类型是什么。

Typescript 将作为in 方法 ( ) 和箭头函数 ( )this的实例键入,但将作为 any in键入。如果您尝试在下面使用,这将导致错误Greetergreetgreet2thisgreet3thisgreet3noImplictAny

何时使用它们

  1. 如果此函数不会作为事件处理程序传递给另一个组件,请使用方法语法(除非您使用bind或其他东西来确保this仍然是类的实例)

  2. 当您的函数将被传递给其他组件并且您需要访问this函数内部时,请使用箭头函数语法。

  3. 真的想不出一个好的用例,通常避免。

于 2019-05-09T09:28:30.043 回答
6

this关键词区别:

在上面所有三个都相同this,但是当您将方法传递给另一个函数时,您会看到不同之处。

class Greeter {
  constructor() {
  }
  greet() {
    console.log(this);
  }

  greet2 = () => {
    console.log(this);
  }

  greet3 = function() {
    console.log(this);
  }
}


let bla = new Greeter();
function wrapper(f){
  f();
}
wrapper(bla.greet) //undefined
wrapper(bla.greet2) //Greeter 
wrapper(bla.greet3) //undefined

但是还有另一个区别,第一种方法是 on the prototypeclass而其他两种方法不是。它们是对象实例的方法。

class Greeter {
  constructor() {
  }
  greet() {
    console.log('greet1', this);
  }

  greet2 = () => {
    console.log('greet2', this);
  }

  greet3 = function() {
    console.log('greet3', this);
  }
}

let bla = new Greeter();
console.log(Object.getOwnPropertyNames(Greeter.prototype))

如果我在课堂上 ->str = "my string";并且在我可以说的所有 3 种方法中console.log(this.str),它会输出“我的字符串”。但我想知道 - 这真的是一回事吗

不,它们不是相同的东西。正如我提到的那样greet2,它们greet3不会Greeter.prototype在实例本身上。这意味着如果您创建它们的1000实例,Greeter它们将在内存中存储1000 个不同的方法(greet2和),用于 1000 个不同的实例。greet3但是greet所有实例都会有一个方法。

请参阅下面的片段,其中包含两个实例Greeter()

于 2019-05-09T08:59:19.013 回答