繁体   English   中英

类方法与类字段函数与类字段箭头函数有什么区别?

[英]What is the difference between class method vs. class field function vs. class field arrow function?

类方法、作为函数的类属性和作为箭头函数的类属性有什么区别? 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。

所有 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 个函数调用都将按预期打印:在bla调用时, 'greet* me'

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

运行时这是谁

箭头功能捕捉this从声明上下文,所以thisgreet2始终保证是创建此函数的类的实例。 其他版本(方法和函数)没有这样的保证。

因此,在此代码中,并非所有 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实例化时分配一个新的闭包。

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

打字稿将键入this作为实例Greeter在方法(两者greet )和箭头功能( greet2 ),但将输入this作为任何在greet3 这将使一个错误,如果你尝试使用thisgreet3noImplictAny

何时使用它们

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

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

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

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

但是还有一个区别,第一个方法在classprototype ,而其他两个不在。 它们是对象实例的方法。

 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)并输出“我的字符串”。 但我想知道 - 这真的是同一件事吗

不,它们不是同一回事。 正如我提到的, greet2greet3不会在Greeter.prototype而是在实例本身上。 这意味着如果你创建1000 Greeter实例,它们将有 1000 个不同的方法( greet2greet3 )存储在内存中,用于 1000 个不同的实例。 但是所有实例都有一个greet方法。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM