Typescript中的类方法与属性函数与属性箭头函数之间有什么区别?

时间:2019-05-09 08:51:37

标签: javascript typescript oop

我想知道-类方法,作为函数的类属性和作为箭头函数的类属性之间有什么区别?在不同的方法变体中,“ 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();

编辑:已编译的打字稿的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

2 个答案:

答案 0 :(得分:2)

所有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,因此始终保证this中的greet2是创建此函数的类实例。其他版本(方法和功能)不提供此类保证。

因此在此代码中,并非所有3个都打印相同的文本:

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

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

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

分配功能的地方

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

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

Typescript将在方法(this)和箭头函数(Greeter)中将greet键入为greet2的实例,但将键入thisgreet3中的任何内容一样。如果您尝试在this下的greet3中使用noImplictAny

,则会出错

何时使用它们

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

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

  3. 为此,真的不能想到一个好的用例,通常避免使用。

答案 1 :(得分:1)

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

但是还有另一个区别,第一种方法在prototype的{​​{1}}上,而其他两种不在。它们是对象实例的方法。

class

  

如果我在类-> 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";并输出“我的字符串”。但我想知道-这真的是同一件事

不,他们不是同一个人。正如我提到的,console.log(this.str)greet2将不在greet3上,而将在实例本身上。这意味着,如果您创建Greeter.prototype的{​​{1}}个实例,它们将在内存中存储1000种不同方法(1000Greeter),用于1000种不同实例。但是所有实例都只有一个greet2方法。

请参见下面的摘录,其中包含两个greet3实例