类(Class)
优先使用ES2015/ES6类而非ES5普通函数
传统的ES5类很难实现可读性良好的类继承结构、构造函数和方法定义。如果需要使用继承(注意可能不需要),应优先选择ES2015/ES6类。但是,在确实需要更大更复杂的对象之前,应优先使用小函数而不是类。
反面示例:
javascript
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() {};正面示例:
javascript
class Animal {
constructor(age) {
this.age = age;
}
move() {
/* ... */
}
}
class Mammal extends Animal {
constructor(age, furColor) {
super(age);
this.furColor = furColor;
}
liveBirth() {
/* ... */
}
}
class Human extends Mammal {
constructor(age, furColor, languageSpoken) {
super(age, furColor);
this.languageSpoken = languageSpoken;
}
speak() {
/* ... */
}
}使用方法链式调用
这种模式在JavaScript中非常实用,常见于jQuery和Lodash等库。它使代码更简洁且表达力强。实现方式很简单:在每个类方法末尾返回this,即可支持链式调用。
反面示例:
javascript
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();正面示例:
javascript
class Car {
constructor(make, model, color) {
this.make = make;
this.model = model;
this.color = color;
}
setMake(make) {
this.make = make;
// NOTE: Returning this for chaining
return this;
}
setModel(model) {
this.model = model;
// NOTE: Returning this for chaining
return this;
}
setColor(color) {
this.color = color;
// NOTE: Returning this for chaining
return this;
}
save() {
console.log(this.make, this.model, this.color);
// NOTE: Returning this for chaining
return this;
}
}
const car = new Car("Ford", "F-150", "red").setColor("pink").save();优先选用组合而非继承
正如四人组在《设计模式》中提出的原则,在可行时应优先选择组合而非继承。继承和组合都有适用场景。核心原则是:如果本能地想用继承,应考虑组合是否能更好地建模问题,在某些情况下确实如此。
你可能疑惑"何时应该使用继承?"这取决于具体问题,以下是适合继承的场景:
- 继承关系表示的是"is-a"关系而非"has-a"关系(Human->Animal属于is-a,User->UserDetails属于has-a)
- 可以复用基类代码(例如Human可以像所有动物一样移动)
- 希望通过修改基类来全局调整派生类(例如修改所有动物移动时的热量消耗)
反面示例:
javascript
class Employee {
constructor(name, email) {
this.name = name;
this.email = email;
}
// ...
}
// Bad because Employees "have" tax data. EmployeeTaxData is not a type of Employee
class EmployeeTaxData extends Employee {
constructor(ssn, salary) {
super();
this.ssn = ssn;
this.salary = salary;
}
// ...
}正面示例:
javascript
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);
}
// ...
}