Cours 1 – Compléments avancés sur le langage Curiosités de JavaScript

Quelques points supplémentaires

Liens externes / Bibliographie :

Pêle-mêle, quelques spécificités avancées du langage JavaScript :

Objets et prototypes (version rapide)

Prototype

Tout objet JS possède un attribut __proto__ qui contient un objet (ou null) que l’on appelle son prototype.

Quand on veut accéder à une propriété d’un objet en lecture, JS cherche la propriété dans l’objet. S’il ne la trouve pas, il va la chercher dans le prototype. S’il ne la trouve toujours pas, dans le prototype du prototype et ainsi de suite jusqu’à arriver au prototype null.

let animal = {
  eats: true
};
let rabbit = {
  jumps: true
};

rabbit.__proto__ = animal; // On défini le prototype de rabbit à animal

alert( rabbit.jumps ); // true
alert( rabbit.eats ); // true
// La propriété eats n'est pas trouvé dans rabbit, donc JS va la chercher dans son prototype (animal) et la trouve

Source sur JavaScript.info : prototype inheritance

Syntaxe class

Dans le code

class User {
  constructor(name) { this.name = name; }
  sayHi() { alert(this.name); }
}

let user = new User("Brandon")

la ligne let user = new User("Brandon") est à peu près équivalente à

user = {}
user.name = "Brandon"
user.__proto__ = {
  constructor: function (name) { this.name = name; }
  sayHi: function() { alert(this.name); }
}

Pour être un peu plus précis, le prototype est créé une fois pour toute à la déclaration de la classe. La syntaxe équivalente serait donc

// Code équivalent à `class User { ... }`
function User(name) {
  this.name = name;
}
// Les fonctions sont des objets et possèdent donc des propriétés
User.prototype = { 
  constructor: User,
  sayHi: function() { alert(this.name); }
}

// Code équivalent à `let user = new User("Brandon")`
user = {}
user.name = "Brandon"
user.__proto__ = User.prototype

Objets et prototypes (version longue)

Constructeur new

On peut utiliser new sur n’importe quelle fonction. Dans le code suivant,

function User(name) {
  this.name = name;
  this.isAdmin = false;
}

let user = new User("Jack");

l’exécution de new User("Jack"); est équivalent à l’exécution de la fonction suivante, qui est basée sur la fonction User en rajoutant quelques lignes

function () {
  this = {}; // rajout implicite
  
  // Code de la fonction User
  this.name = name;
  this.isAdmin = false;
  
  return this; // rajout implicite
}

Source sur JavaScript.info : constructeur new

Prototype d’une fonction

Les fonctions sont des objets, ils peuvent donc posséder des attributs. Si une fonction contient un attribut prototype, alors cet attribut sera utilisé pour fixer le prototype d’un objet créé par new.

En pratique, dans le code suivant,

function Rabbit(name) {
  this.name = name;
}

Rabbit.prototype = {eats: true};

let rabbit = new Rabbit("Lapin");

alert( rabbit.eats ); // true

l’exécution de new Rabbit("Lapin"); est équivalent à l’exécution de la fonction suivante, qui est basée sur la fonction Rabbit en rajoutant quelques lignes

function () {
  this = {}; // Rajout implicite
  
  // Code de la fonction Rabbit
  this.name = name;
 
  // Rajouts implicites
  this.__proto__ = Rabbit.prototype; 
  return this;
}

F.prototype par défaut

Toute fonction a un attribut prototype par défaut, qui est un objet dont la seule propriété constructor repointe sur la fonction.

function Rabbit() {}

// Propriété 'prototype' par défaut, rajouté implicitement
// Rabbit.prototype = { constructor: Rabbit };

Syntaxe équivalente au sucre syntaxique class

Une class est juste une autre syntaxe pour écrire une fonction qui aura pour but d’être utilisée avec new.

class User {
  constructor(name) { this.name = name; }
  sayHi() { alert(this.name); }
}

est à peu près équivalent à

// rewriting class User in pure functions

// 1. Create constructor function
function User(name) {
  this.name = name;
}
// a function prototype has "constructor" property by default,
// so we don't need to create it

// 2. Add the method to prototype
User.prototype.sayHi = function() {
  alert(this.name);
};

Quelques différences par rapport à la syntaxe à base de fonction :

  1. la fonction créée par class User ne peut être appelée qu’avec new.
  2. les classes utilisent use strict
  3. les méthodes de classes ne sont pas énumérables pour ne pas les voir quand on fait for..in.

Source sur JavaScript.info : class