Skip to content

Classes

Privilégier les classes ES2015/ES6 aux fonctions simples ES5

Il est très difficile d'obtenir un héritage de classes, des constructeurs et des méthodes lisibles avec les classes ES5 classiques. Si vous avez besoin d'héritage (sachez que ce n'est peut-être pas nécessaire), privilégiez les classes ES2015/ES6. Toutefois, préférez les petites fonctions aux classes jusqu'à ce que vous ayez besoin d'objets plus volumineux et complexes.

Mauvais:

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

Bon:

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

Utiliser le chaînage de méthodes

Ce modèle, très répandu en JavaScript (comme dans jQuery et Lodash), rend votre code plus expressif et moins verbeux. Dans les fonctions de classe, retournez simplement this à la fin de chaque méthode pour permettre l'enchaînement des appels.

Mauvais:

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

Bon:

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

Préférer la composition à l'héritage

Comme l'indique le célèbre ouvrage Design Patterns du « Gang of Four », privilégiez la composition plutôt que l'héritage lorsque possible. Si votre premier réflexe est d'utiliser l'héritage, évaluez si la composition modéliserait mieux votre problème - ce sera parfois le cas.

Vous pourriez alors demander : « Quand utiliser l'héritage ? » Cela dépend du contexte, mais voici des cas pertinents :

  1. Votre héritage représente une relation « est-un » et non « a-un » (Humain→Animal vs Utilisateur→DétailsUtilisateur)
  2. Vous pouvez réutiliser le code des classes mères (les humains se déplacent comme tous les animaux)
  3. Vous voulez modifier globalement les classes dérivées via la classe mère (modifier la dépense calorique de tous les animaux en mouvement)

Mauvais:

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

  // ...
}

Bon:

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