ES2015/ES6 类优先与 ES5 纯函数

很难为经典的 ES5 类创建可读的的继承、 构造和方法定义。 如果你需要继承(并且感到奇怪为啥你不需
要), 则优先用 ES2015/ES6的类。 不过, 短小的函数优先于类, 知道你发现你需要更大并且更复杂的
对象。

不好的:

  1. const Animal = function(age) {
  2. if (!(this instanceof Animal)) {
  3. throw new Error('Instantiate Animal with `new`');
  4. }
  5. this.age = age;
  6. };
  7. Animal.prototype.move = function move() {};
  8. const Mammal = function(age, furColor) {
  9. if (!(this instanceof Mammal)) {
  10. throw new Error('Instantiate Mammal with `new`');
  11. }
  12. Animal.call(this, age);
  13. this.furColor = furColor;
  14. };
  15. Mammal.prototype = Object.create(Animal.prototype);
  16. Mammal.prototype.constructor = Mammal;
  17. Mammal.prototype.liveBirth = function liveBirth() {};
  18. const Human = function(age, furColor, languageSpoken) {
  19. if (!(this instanceof Human)) {
  20. throw new Error('Instantiate Human with `new`');
  21. }
  22. Mammal.call(this, age, furColor);
  23. this.languageSpoken = languageSpoken;
  24. };
  25. Human.prototype = Object.create(Mammal.prototype);
  26. Human.prototype.constructor = Human;
  27. Human.prototype.speak = function speak() {};

好的

  1. class Animal {
  2. constructor(age) {
  3. this.age = age;
  4. }
  5. move() { /* ... */ }
  6. }
  7. class Mammal extends Animal {
  8. constructor(age, furColor) {
  9. super(age);
  10. this.furColor = furColor;
  11. }
  12. liveBirth() { /* ... */ }
  13. }
  14. class Human extends Mammal {
  15. constructor(age, furColor, languageSpoken) {
  16. super(age, furColor);
  17. this.languageSpoken = languageSpoken;
  18. }
  19. speak() { /* ... */ }
  20. }

使用方法链

这个模式在 JavaScript 中是非常有用的, 并且你可以在许多类库比如 jQuery 和 Lodash 中见到。
它允许你的代码变得富有表现力, 并减少啰嗦。 因为这个原因, 我说, 使用方法链然后再看看你的代码
会变得多么简洁。 在你的类/方法中, 简单的在每个方法的最后返回 this , 然后你就能把这个类的
其它方法链在一起。

不好的:

  1. class Car {
  2. constructor() {
  3. this.make = 'Honda';
  4. this.model = 'Accord';
  5. this.color = 'white';
  6. }
  7. setMake(make) {
  8. this.make = make;
  9. }
  10. setModel(model) {
  11. this.model = model;
  12. }
  13. setColor(color) {
  14. this.color = color;
  15. }
  16. save() {
  17. console.log(this.make, this.model, this.color);
  18. }
  19. }
  20. const car = new Car();
  21. car.setColor('pink');
  22. car.setMake('Ford');
  23. car.setModel('F-150');
  24. car.save();

好的:

  1. class Car {
  2. constructor() {
  3. this.make = 'Honda';
  4. this.model = 'Accord';
  5. this.color = 'white';
  6. }
  7. setMake(make) {
  8. this.make = make;
  9. // NOTE: Returning this for chaining
  10. return this;
  11. }
  12. setModel(model) {
  13. this.model = model;
  14. // NOTE: Returning this for chaining
  15. return this;
  16. }
  17. setColor(color) {
  18. this.color = color;
  19. // NOTE: Returning this for chaining
  20. return this;
  21. }
  22. save() {
  23. console.log(this.make, this.model, this.color);
  24. // NOTE: Returning this for chaining
  25. return this;
  26. }
  27. }
  28. const car = new Car()
  29. .setColor('pink')
  30. .setMake('Ford')
  31. .setModel('F-150')
  32. .save();

组合优先于继承

正如设计模式四人帮所述, 如果可能,
你应该优先使用组合而不是继承。 有许多好的理由去使用继承, 也有许多好的理由去使用组合。这个格言
的重点是, 如果你本能的观点是继承, 那么请想一下组合能否更好的为你的问题建模。 很多情况下它真的
可以。

You might be wondering then, “when should I use inheritance?” It
depends on your problem at hand, but this is a decent list of when inheritance
makes more sense than composition:

那么你也许会这样想, “我什么时候改使用继承?” 这取决于你手上的问题, 不过这儿有一个像样的列表说
明什么时候继承比组合更好用:

  1. 你的继承表示”是一个”的关系而不是”有一个”的关系(人类->动物 vs 用户->用户详情);
  2. 你可以重用来自基类的代码(人可以像所有动物一样行动);
  3. 你想通过基类对子类进行全局的修改(改变所有动物行动时的热量消耗);

不好的:

  1. class Employee {
  2. constructor(name, email) {
  3. this.name = name;
  4. this.email = email;
  5. }
  6. // ...
  7. }
  8. // 不好是因为雇员“有”税率数据, EmployeeTaxData 不是一个 Employee 类型。
  9. class EmployeeTaxData extends Employee {
  10. constructor(ssn, salary) {
  11. super();
  12. this.ssn = ssn;
  13. this.salary = salary;
  14. }
  15. // ...
  16. }

好的:

  1. class EmployeeTaxData {
  2. constructor(ssn, salary) {
  3. this.ssn = ssn;
  4. this.salary = salary;
  5. }
  6. // ...
  7. }
  8. class Employee {
  9. constructor(name, email) {
  10. this.name = name;
  11. this.email = email;
  12. }
  13. setTaxData(ssn, salary) {
  14. this.taxData = new EmployeeTaxData(ssn, salary);
  15. }
  16. // ...
  17. }