Héritage et classes en JavaScript (bis)

Un jour, Guido a dit que Modern JavaScript for the Impatient était un bon livre.

Comme tout ce que dit Guido est vrai, j'ai donc acheté ce livre. Mieux, j'ai même commencé à le lire.

Et c'est vrai que le livre est bon.

Mais contrairement à ce que laisse penser son titre, il vaut mieux être patient si vous décidez de faire les exercices.

C'est d'ailleurs un exercice du chapitre 4 qui m'a donné une idée :

Draw a diagram of SavingAccount and CheckingAccount objects from the preceding exercice, similar to Figure 4-4.

La représentation visuelle aide beaucoup et je me suis dit que serait sympa de pousser le périmètre du diagramme pour illustrer en même temps le billet "Héritage et classes en JavaScript" et montrer toute la chaîne des prototypes.

Vous trouverez le diagramme juste après le code.

Le code

class BankAccount {
  constructor(balance = 0) {
    this.balance = balance
  }
  get balanceInt() { return Math.floor(this.balance) }
}

class SavingsAccount extends BankAccount {
  constructor(balance = 0, interest = 5) {
    super(balance)
    this.interest = interest
  }
  addInterest() {
    this.balance *= 1 + this.interest / 100
  }
}

class CheckingAccount extends SavingsAccount {
  constructor(balance = 0, interest = 5, fee = 5) {
    super(balance, interest)
    this.fee = fee
  }
  withdraw(amount) {
    console.log(`Withdrawing an amount of ${amount}. Cost is ${this.fee}.`)
    const amountToWithdraw = amount + this.fee
    this.balance -= amountToWithdraw
  }
}

const account1 = new CheckingAccount(100, 10, 2)

const account2 = new CheckingAccount(50, 4, 5)

Diagramme de la chaîne des prototypes

JavaScript n'a pas vraiment de système de classe.

À la place, on utilise l'opérateur new avec des fonctions qu'on appelle alors "fonctions constructrices" (new.target peut être utilisé pour forcer l'utilisation de new).

La syntaxe avec class et extends dans le code ci-dessus est un sucre syntaxique pour créer des fonctions constructrices.

Toute fonction en JavaScript a une propriété prototype qui pointe vers un objet prototype créé automatiquement. On peut y stocker des méthodes et des propriétés.

Une fonction constructrice produit des objets qui partagent son objet prototype.

Les prototypes sont chaînables.

Si aucun résultat n'est trouvé, une recherche est effectuée dans la chaîne des prototypes. Cette recherche ne fonctionne qu'en lecture. En écriture, la valeur est toujours mise à jour dans l'objet lui-même !

Le diagramme ci-dessous représente la chaîne des prototypes avec les fonctions constructrices à gauche :

Héritage et classes en JavaScript

Afficher la chaîne des prototypes

function printPrototypeChainOf(obj) {
    let proto = Object.getPrototypeOf(obj)
    let result = ''
    while (proto) {
        if (result) {
          result += ' => '
        }
        if (typeof proto === 'function' && proto.name) {
          result += `${proto.name}`
        } else {
          // This could be an object or `Function.prototype` or `Object.prototype`.
          result += `${proto.constructor.name}.prototype`
        }
        proto = Object.getPrototypeOf(proto)
    }
    console.log(result)
}

printPrototypeChainOf(account1)
// CheckingAccount.prototype => SavingsAccount.prototype => BankAccount.prototype => Object.prototype

printPrototypeChainOf(account2)
// CheckingAccount.prototype => SavingsAccount.prototype => BankAccount.prototype => Object.prototype

printPrototypeChainOf(CheckingAccount)
// SavingsAccount => BankAccount => Function.prototype => Object.prototype

Conclusion

C'était vraiment très intéressant.

Avant La playlist City Pop Après Programmation asynchrone en JavaScript

Tag Kemar Joint