代码整洁之道——4、类

一、优先使用ES6语法的类而不是ES5的纯函数

传统的ES5类语法很难拥有类的继承、构造函数和方法的定义。如果你需要使用继承(要注意的是,你可能不需要),那么就用ES2015/ES6的类。但是,在你发现你需要更大更复杂的对象的之前,尽量使用小巧的函数。

Bad:
const Animal = function(age) {
  if (!(this instanceof Animal)) {
    throw new Error('Instantiate Animal with `new`');
  }

  this.age = age;
};

Animal.prototype.move = function move() {};

const Mammal = function(age, furColor) {
  if (!(this instanceof Mammal)) {
    throw new Error('Instantiate Mammal with `new`');
  }

  Animal.call(this, age);
  this.furColor = furColor;
};

Mammal.prototype = Object.create(Animal.prototype);
Mammal.prototype.constructor = Mammal;
Mammal.prototype.liveBirth = function liveBirth() {};

const Human = function(age, furColor, languageSpoken) {
  if (!(this instanceof Human)) {
    throw new Error('Instantiate Human with `new`');
  }

  Mammal.call(this, age, furColor);
  this.languageSpoken = languageSpoken;
};

Human.prototype = Object.create(Mammal.prototype);
Human.prototype.constructor = Human;
Human.prototype.speak = function speak() {};


Good:
//定义一个动物类,有age属性和move方法
class Animal {
  constructor(age) {
    this.age = age;
  }
  move() { /* ... */ }
}
//定义一个哺乳动物类继承动物类,除了animal的属性和方法外还有自己的liveBirth方法
class Mammal extends Animal {
  constructor(age, furColor) {
    super(age);
    this.furColor = furColor;
  }

  liveBirth() { /* ... */ }
}

//定义人类这个类继承哺乳动物,除哺乳动物的属性和方法外,还有自己的languageSpoken属性和speak方法
class Human extends Mammal {
  constructor(age, furColor, languageSpoken) {
    super(age, furColor);
    this.languageSpoken = languageSpoken;
  }

  speak() { /* ... */ }
}

二、使用方法链

这个模式在JS中非常有用,你可以在很多像jquery、lodash等库中看到。它可以让你的代码简洁而富有表现力。正是因为这个原因,所以我说使用方法链,然后看下你的代码可以变得多么简洁。在你的类函数中,在每个函数的最后简单的返回this,你就可以把这个类的方法链在一起。

Bad:
class Car {
  constructor(make, model, color) {
    this.make = make;
    this.model = model;
    this.color = color;
  }

  setMake(make) {
    this.make = make;
  }

  setModel(model) {
    this.model = model;
  }

  setColor(color) {
    this.color = color;
  }

  save() {
    console.log(this.make, this.model, this.color);
  }
}

const car = new Car('Ford','F-150','red');
car.setColor('pink');
car.save();

Good:
class Car {
  constructor(make, model, color) {
    this.make = make;
    this.model = model;
    this.color = color;
  }

  setMake(make) {
    this.make = make;
    // 为链式调用返回this
    return this;
  }

  setModel(model) {
    this.model = model;
    // 为链式调用返回this
    return this;
  }

  setColor(color) {
    this.color = color;
    // 为链式调用返回this
    return this;
  }

  save() {
    console.log(this.make, this.model, this.color);
   // 为链式调用返回this
    return this;
  }
}

//链式调用
const car = new Car('Ford','F-150','red')
  .setColor('pink')
  .save();

三、优先使用组合而不是继承

正如著名的设计模式(Design Patterns )所论述的一样,你应该尽可能的使用组合而不是继承。使用组合和继承的优点都有很多。这则论述的主要观点是:如果你本来想用继承,那么想想组合能不能替代,大部分情况下都是可以的。

你可能会想,什么时候我需要使用继承呢?这取决于你手头的问题,这有个列表说明什么时候使用继承比使用组合好:

1、你的继承代表的“是一个”的关系而不是“有一个”的关系。(人类继承自动物,用户有用户明细Human->Animal vs. User->UserDetails)

2、你可以重用基类的代码。(人类像动物一样拥有move方法)

3、你想要通过改变积累来全局(改变子类继承自基类的方法)(改变动物运动时的热量消耗)

Bad:
class Employee {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }

  // ...
}

//不好是因为EmployeeTaxData 并不是Employee 的一种类型。而是Employee 有EmployeeTaxData 。
class EmployeeTaxData extends Employee {
  constructor(ssn, salary) {
    super();
    this.ssn = ssn;
    this.salary = salary;
  }

  // ...
}


Good:
class EmployeeTaxData {
  constructor(ssn, salary) {
    this.ssn = ssn;
    this.salary = salary;
  }

  // ...
}

class Employee {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }
//员工有税率数据
  setTaxData(ssn, salary) {
    this.taxData = new EmployeeTaxData(ssn, salary);
  }
  // ...
}

 

posted on 2017-07-26 12:16  小小驰  阅读(160)  评论(0编辑  收藏  举报