this React answer 中的观点在 Angular、任何其他框架或原生 JavaScript/TypeScript 中仍然有效。
类原型方法是 ES6,类箭头方法不是。箭头方法属于class fields proposal,而不是现有规范的一部分。它们是用 TypeScript 实现的,也可以用 Babel 进行转译。
通常使用原型method() { ... } 比使用箭头method = () => { ... } 更可取,因为它更灵活。
回调
箭头方法提供的唯一真正机会是它可以无缝地用作回调:
class Class {
method = () => { ... }
}
registerCallback(new Class().method);
如果原型方法应该用作回调,它应该被额外绑定,这应该最好在构造函数中完成:
class Class {
constructor() {
this.method = this.method.bind(this);
}
method() { ... }
}
registerCallback(new Class().method);
可以在 TypeScript 和 ES Next 中使用像 bind-decorator 这样的装饰器,为构造函数中的方法绑定提供更简洁的替代方案:
import bind from 'bind-decorator';
class Class {
@bind
method() { ... }
}
继承
箭头方法也限制子类使用箭头方法,否则它们不会被覆盖。如果忽略了箭头,就会产生问题:
class Parent {
method = () => { ... }
}
class Child extends Parent {
method() { ... } // won't override Parent method
}
不能在子类中使用super.method(),因为super.method 引用了不存在的Parent.prototype.method:
class Parent {
method = () => { ... }
}
class Child extends Parent {
method = () => {
super.method(); // won't work
...
}
}
混合
原型方法可以有效地用于 mixins。 Mixin 对于多重继承或修复 TypeScript 方法可见性问题很有用。
由于箭头方法在类原型上不可用,因此无法从类外部访问:
class Parent {
method = () => { ... }
}
class Child extends OtherParent { ... }
Object.assign(Child.prototype, Parent.prototype) // method won't be copied
测试
原型方法提供的一个有价值的特性是它们可以在类实例化之前访问,因此它们可以在测试中被监视或模拟,即使它们在构造之后立即被调用:
class Class {
constructor(arg) {
this.init(arg);
}
init(arg) { ... }
}
spyOn(Class.prototype, 'init').and.callThrough();
const object = new Class(1);
expect(object.init).toHaveBeenCalledWith(1);
当方法是箭头时,这是不可能的。
TL;DR:原型和箭头类方法之间的选择似乎是一个品味问题,但实际上原型方法的使用更具远见。您可能通常希望避免使用箭头类方法,除非您确定它们不会造成不便。如果您将原型方法作为回调传递,请不要忘记在原型方法上使用 bind。