logo le blog invivoo blanc

L’opérateur new et les constructeurs en Javascript

14 septembre 2018 | Front-End | 0 comments

Cet article est la continuation de mon précèdent article sur l’opérateur this en Javascript. Nous avons vu précédemment que le mot clé this et les appels à fonction d’un objet sont en fait du sucre syntaxique qui cache des appels à des fonctions telles que apply() et call(). Ici, on parlera de l’opérateur new et des mots clés class et constructor introduits dans ES6. Ces mots clés sont utilisés par le langage pour donner l’impression de fonctionner comme un langage orienté objet classique.

Un objet est un dictionnaire

Pour commencer, on reprend la notion d’objet en Javascript. Dans un langage orienté objet classique, une classe définit un ensemble de propriétés et comportements (méthodes) hérité par ses objets. Un objet est une instance d’une classe, dans le sens où ils héritent des propriétés et méthodes définies par la classe.

Mais Javascript n’est pas un langage orienté objet classique, car en Javascript il n’y a pas de classes !

Tout d’abord, un objet en Javascript n’est qu’un dictionnaire qui mappe les noms de ses propriétés en ses valeurs respectives.

Prenons l’exemple suivant, où on définit une classe A avec un constructeur qui set une propriété name et une méthode sayHi() qui affiche la valeur de name.

    class A {
        constructor() {
            this.name = "Marcos";
        }
        sayHi() {
            console.log('Hi ' + this.name + "!");
        }
    }

    const obj = new A();
    obj.sayHi();
    console.log(A);

Sortie :

    Hi Marcos!
    [Function: A]

Le code suivant est équivalent au code précèdent. Là on définit un dictionnaire obj, avec deux clefs : name et sayHi.

    const obj = { 
        name: "Marcos",
        sayHi: function() {
            console.log('Hi ' + this.name + "!");
        }
    };

    obj.sayHi();

Sortie :

    Hi Marcos!

On pourrait aller encore plus loin et modifier la valeur de name avant d’apler sayHi(). On verra que la méthode sayHi() affichera le même texte peu importe si on a modifié la valeur de name en utilisant la syntaxe dictionnaire (1) ou la syntaxe objet.

    ...

    1. obj['name'] = 'Toto';
    2. obj.name = "Toto";

    obj.sayHi();

Une classe est un constructeur, un constructeur est une fonction

Un objet n’est pas une instance d’une classe, mais une instance d’une fonction : son constructeur.

Si on exécute le code suivant, on verra que A n’est pas une classe, mais une fonction avec deux paramètres. Les deux paramètres de son constructeur.

    class A {
        constructor(name, age) {
            this.name = name;
            this.age = age;
        }
    }

    console.log(A, A.length);

    const obj = new A("Marcos", 34);
    console.log(obj);

Sortie:

[Function: A] 2
A { name: 'Marcos', age: 34 }

Quand on appelle un constructeur, on appelle en fait une fonction. Le moteur d’exécution se charge de setter la valeur de this comme étant l’objet qui est en train d’être créé.

Si on exécute le code suivant, on verra que le résultat sera le même.

    function A(name, age) {
        this.name = name;
        this.age = age;
    }

    console.log(A, A.length);

    const obj = new A("Marcos", 34);
    console.log(obj);

Piège : si A est une fonction, je peux l’appeler directement…

Eh bien oui. Essayons le code suivant :

    function A() {
        console.log("exécutée");
        this.name = "Marcos";
    }

    const obj = A();
    console.log(obj);

Sortie: 

exécutée
undefined

On verra que obj est undefined même si la fonction A a bien été appelée ! Si vous vous rappelez de l’article précèdent, vous savez ce qui s’est passé : on a setté la valeur de name dans l’objet global/window.

Le code suivant illustre ça :

    function A() {
        console.log("exécutée");
        this.name = "Marcos";
    }

    const obj = A();
    console.log(this.name);

Sortie :

exécutée
Marcos

Et si on essayait la même chose avec la syntaxe class ?

    class A {
        constructor() {
            this.name = "Marcos";
        }
    }

    const obj = A();
    console.log(obj);

Sortie :

[stdin]:7
    const obj = A();
                ^

TypeError: Class constructor A cannot be invoked without 'new'
    at [stdin]:7:17
    at Script.runInThisContext (vm.js:91:20)
    at Object.runInThisContext (vm.js:298:38)
    at Object. ([stdin]-wrapper:6:22)
    at Module._compile (internal/modules/cjs/loader.js:689:30)
    at evalScript (internal/bootstrap/node.js:563:27)
    at ReadStream. (internal/bootstrap/node.js:306:15)
    at ReadStream.emit (events.js:187:15)
    at endReadableNT (_stream_readable.js:1081:12)
    at process._tickCallback (internal/process/next_tick.js:63:19)

Et là on trouvera la première différence entre un constructeur et une fonction. On verra que le moteur Javascript détecte l’utilisation d’un constructeur sans le mot clé new et produit une erreur.

Pour aller plus loin

Dans cet article on a vu l’opérateur new et sa relation avec les constructeurs et classes en Javascript. C’est pourquoi dans cette dernière partie, je liste quelques bonnes références où vous pouvez voir, plus en détails le fonctionnement de l’orientation objets en Javascript. Dans la première section je liste des articles qui se contentent d’expliquer la spéc. Puis, je liste des articles qui présentent les avantages et inconvénients de ce modèle. Et pour finir, vous trouverez des articles qui se concentrent sur les bonnes pratiques à adopter.

Specs

Articles critiques

Quoi faire ?