Skip to content

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:

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() {};

Bueno:

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() {
    /* ... */
  }
}

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:

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();

Bueno:

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();

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:

    1. Tu herencia representa una relación "es-un" y no "tiene-un" (Humano->Animal vs. Usuario->DetallesUsuario).
    1. Puedes reutilizar código de las clases base (Los humanos pueden moverse como todos los animales).
    1. Quieres hacer cambios globales en clases derivadas modificando una clase base (Cambiar el gasto calórico de todos los animales al moverse).

Malo:

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;
  }

  // ...
}

Bueno:

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);
  }
  // ...
}