Le Scope en JavaScript - JS - Sous Le Capot - Partie 4

Le scope en Javascript : définition et explications.
Arthur.jpg
Arthur COMBE, JavaScript loverMis à jour le 10 Août 2020
javascript scope block

Quatrième article de notre série : Javascript sous le capot ! Pour l’occasion, on fait un focus sur le scope en Javascript : scope de fonction et block scope. Bonne lecture !

4 – Le Scope en Javascript

Nous avons déjà entendu parler de Scope dans la partie 2 – Fonctionnement du moteur. Il s’occupait de collecter et garder une liste des variables déclarées. On avait également dit qu’il pouvait y avoir plusieurs Scopes.

Il y a même plusieurs types de Scope, allons donc voir ici plus en détail, ce concept.

Scope de fonction - Javascript

La vision la plus simple pour commencer est de voir chaque fonction comme un Scope.

Chaque fonction va créer une bulle autour d’elle (le Scope), et seulement elle et ce qu’elle contient, pourra y accéder.

On peut voir les Scopes comme une succession de bulles, imbriquées les unes dans les unes. Une bulle parent ne pourra pas voir ce que contient les bulles enfants, seul l’inverse est possible : une bulle peut accéder à toutes les bulles qui l’englobe, mais aucune qu’elle englobe elle.

Un exemple très simple afin d’illustrer tout ça :

function bar(){
    var a = 3;

    function foo(){
        var b = 5;
        console.log(a); // 3
    }

    foo();
    console.log(b); // ReferenceError
}
bar()

Dans le code ci-dessus, on voit que le Scope de bar contient a et foo(), et foo étant une fonction, elle a son propre Scope contenant lui-même b.
bar a donc accès uniquement à a et foo car b est à l’intérieur de sa bulle. Quant à lui, foo à accès a tout ce qui se trouve à l’extérieur, il peut donc utiliser a, en plus de b.

Cela a autant d’avantages que d’inconvénients : on peut avoir des variables facilement globales à une grande partie de code, seulement, ça peut être plus compliqué de gérer un très grand nombre de variables qui ne servent pas forcément partout, et c’est moins lisible en termes de clarté de code.

Block Scope - Javascript

Même si le Scope de fonction est le type de Scope le plus commun, il en existe d’autres, dont un que vous avez surement déjà utilisé sans même vous en rendre compte : le Block scope.

Depuis ES6, deux nouveaux mots clés sont introduits : let et const.

Ces mots clés, qui ont l’air de fonctionner de la même manière que var (à part const qui a la particularité d’être finale), ont en fait une différence notable : ils attachent la variable au Scope du block auquel il est attaché. Un block est tout simplement une paire d’accolade { .. }, tel qu’un if ou for.

var foo = true;

if (foo) {
    let bar = 2; 
    console.log(bar);
}

console.log(bar); // ReferenceError

Cela peut vous paraître logique, mais, si à l’inverse vous déclarez bar avec un var à la place de let, vous obtiendrez 2 au lieu de ReferenceError au deuxième console.log.

Il est également possible d’utiliser des accolades sans mot clé, appelé explicit block, afin de juste créer un Scope :

var foo = 3;

{ // <-- explicit block
    let bar = foo * 2;
    console.log(bar);
}

console.log(bar) // ReferenceError

Performances Block Scope - Javascript

Un block scope peut s’avérer particulièrement utile afin de gérer de plus près la mémoire de votre application.

En effet, un block scope sera garbage collected directement à la sortie de celui-ci. C’est-à-dire que la mémoire utilisée sera libérée immédiatement.

var enormeImage = { .. };
process(enormeImage);

var btn = document.getElementById( "my_button" );
btn.addEventListener( "click", function click(evt){
    console.log("button clicked");
}, false);

Si on prend en compte le code ci-dessus, le callback du click n’a pas besoin de enormeImage, la mémoire qu’elle occupe pourrait donc être libérée. Cependant, la fonction click va encapsuler tout le Scope afin de garder le contexte (appelé closure).

En utilisant un block scope, vous pouvez contrôler ce comportement, en vous assurant que la mémoire n’est pas utilisée pour rien :

{
    let enormeImage = { .. };
    process(enormeImage);
}

var btn = document.getElementById( "my_button" );
btn.addEventListener( "click", function click(evt){
    console.log("button clicked");
}, false);

Comme la variable enormeImage est déclarée avec le mot clé let, elle sera attachée au block qui l’englobe (ici un explicit block). De ce fait, cette variable ne pourra jamais être utilisée en dehors de ce Scope, donc la mémoire sera automatiquement libérée.

Scope en JS : pour aller plus loin !

Pour voir ou revoir les autres parties de notre série sur le JS, c’est par ici :