Clases
Prefiere clases ES2015/ES6 sobre funciones simples de ES5
Es muy difícil lograr una herencia de clase legible, construcción y definiciones de métodos con clases clásicas de ES5. Si necesitas herencia (y ten en cuenta que quizás no la necesites), entonces prefiere clases ES2015/ES6. Sin embargo, prefiere funciones pequeñas en lugar de clases hasta que encuentres la necesidad de objetos más grandes y complejos.
Malo:
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() {};Bueno:
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() {
/* ... */
}
}Utiliza el encadenamiento de métodos
Este patrón es muy útil en JavaScript y se ve en bibliotecas como jQuery y Lodash. Permite que tu código sea expresivo y menos verboso. Por eso, recomiendo usar encadenamiento de métodos: observa cuán limpio quedará tu código. En las funciones de tu clase, simplemente retorna this al final de cada función para permitir encadenamiento.
Malo:
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();Bueno:
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();Prefiere composición sobre herencia
Como se menciona en el famoso libro Design Patterns del Gang of Four, debes preferir composición sobre herencia cuando sea posible. Hay muchas razones válidas para usar herencia y muchas para usar composición. El punto clave de este principio es que si tu instinto te lleva hacia la herencia, intenta considerar si la composición podría modelar mejor tu problema. En algunos casos así será.
Podrías preguntarte: "¿Cuándo debería usar herencia?" Depende de tu problema específico, pero esta es una lista adecuada de situaciones donde la herencia tiene más sentido que la composición:
- Tu herencia representa una relación "es-un" y no "tiene-un" (Humano->Animal vs. Usuario->DetallesUsuario).
- Puedes reutilizar código de las clases base (Los humanos pueden moverse como todos los animales).
- Quieres hacer cambios globales en clases derivadas modificando una clase base (Cambiar el gasto calórico de todos los animales al moverse).
Malo:
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;
}
// ...
}Bueno:
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);
}
// ...
}