Passer au contenu principal

Les prototypes en JavaScript

Le prototype est un mécanisme qui permet à un objet d’hériter des propriétés et méthodes d’un autre objet. Contrairement à d’autres langages orientés objet qui utilisent des classes pour définir cet héritage, JavaScript s’appuie sur les prototypes.

Qu’est-ce qu’un prototype ?

Lorsqu’on crée un objet en JavaScript, cet objet hérite automatiquement d’un ensemble de propriétés et de méthodes provenant de son prototype. Ce prototype est un autre objet lié au premier via la chaîne des prototypes. Cela permet de réutiliser des méthodes sans les dupliquer dans chaque objet.

Exemple :

const user = { firstName: 'John', lastName: 'Doe' };

Même si l’objet user ne déclare explicitement que deux propriétés firstName et lastName, il a accès à des méthodes comme toString() ou hasOwnProperty(). Cela est dû au fait que user hérite du prototype natif d’un objet JavaScript appelé Object.prototype

javascript-prototype-1.png

Comment fonctionne l’accès aux propriétés ?

Lorsque vous essayez d’accéder à une propriété ou méthode sur un objet, JavaScript va d’abord chercher cette propriété dans l’objet lui-même. Si elle n’existe pas, il remonte dans la chaîne des prototypes jusqu’à la trouver, ou retourne undefined si elle n’existe nulle part.


Visualiser le prototype d’un objet

Pour examiner le prototype d’un objet, vous pouvez utiliser la méthode Object.getPrototypeOf() :

Le prototype d’un objet dépend de son type. Par exemple, le prototype des chaînes de caractères est String.prototype, celui des tableaux est Array.prototype, et ainsi de suite.

javascript-prototype-2.png

Exemple avec une chaîne de caractères :

console.log(Object.getPrototypeOf("John")); // Affiche String.prototype

Ici, vous avez accès aux méthodes spécifiques aux chaînes de caractères, comme charAt(), toUpperCase(), etc. À leur tour, ces prototypes spécialisés comme String.prototype, Array.prototype ou Date.prototype héritent également de Object.prototype.

javascript-prototype-3.png


Créer un prototype avec des classes

Bien que les prototypes soient la base de l’héritage en JavaScript, les classes introduites avec ECMAScript 6 offrent une syntaxe plus familière aux développeurs venant d’autres langages orientés objet.

Exemple de classe :

class User {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  fullName() {
    return this.firstName + ' ' + this.lastName;
  }
}

const user = new User('John', 'Doe');
console.log(user.fullName()); // Affiche 'John Doe'

Dans cet exemple, User est une classe qui, lorsqu’elle est instanciée avec new User(), crée un objet avec un prototype contenant les méthodes définies dans la classe, comme fullName().

Lorsque vous créez un objet à partir d’une classe, il hérite également d’un prototype, et ce prototype a accès à toutes les méthodes de base via la chaîne des prototypes.

Les classes en JavaScript permettent aussi d’utiliser plusieurs fonctionnalités proches de la Programmation Orientée Objet classique.

get et set

Ils permettent de définir des getters et setters pour gérer la manière dont les propriétés de l’objet sont lues et modifiées.

class User {
  constructor(firstName, lastName) {
    this._firstName = firstName;
    this._lastName = lastName;
  }

  get fullName() {
    return `${this._firstName} ${this._lastName}`;
  }

  set firstName(name) {
    this._firstName = name;
  }
}

const user = new User('John', 'Doe');
console.log(user.fullName); // Accède à la propriété via un getter

user.firstName = 'Jane'; // Modifie la propriété via un setter
console.log(user.fullName); // Affiche 'Jane Doe'

static

Les méthodes statiques sont des méthodes qui appartiennent à la classe elle-même et non à l’instance.

class MathUtil {
  static square(x) {
    return x * x;
  }
}

console.log(MathUtil.square(4)); // Affiche 16

extends et super

Ces mots-clés permettent de créer des classes dérivées pour implémenter l’héritage entre classes.

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  fullName() {
    return `${this.firstName} ${this.lastName}`;
  }
}

class Employee extends Person {
  constructor(firstName, lastName, job) {
    super(firstName, lastName); // Appelle le constructeur de la classe parente
    this.job = job;
  }

  getDetails() {
    return `${this.fullName()} - ${this.job}`;
  }
}

const employee = new Employee('John', 'Doe', 'Developer');
console.log(employee.getDetails()); // Affiche 'John Doe - Developer'