scope-variables.png

Les variables et leurs portées (scopes)

par Romain Barraud· ·

Le scope et déclaration de variables

Dans cet article je ne vais pas avoir les différents types de variables mais plutôt de la différence qu’il existe entre les différents mot clés.

Les variables

Les mots clés

En javascript il existe 3 mot-clés pour déclarer une variable.

  • var
  • let
  • const

Leurs différences se jouent d’abord sur leur déclaration et leurs affectations.

Pour rappel la déclaration d’une variable correspond au code suivant.

const mavariable;
let mavariable2;
var mavariable3;

Pour rappel une affectation correspond au code suivant.

/**
 Debut declaration des variables
**/
const mavariable;
let mavariable2;
var mavariable3;
/**
 Fin declaration des variables
**/

/**
 Debut affectation des variables
**/
mavariable2 = 3;
mavariable3 = "ROMAIN";
/**
 Fin affectation des variables
**/

Le mot clé var

Le mot clé var est le plus ancien mot clé javascript pour déclarer une variable. Il va déclarer une variable dans le scope courant (global ou function).

Il est possible de redéclarer plusieurs fois la même variable

Il est possible de réaffecter plusieurs fois la valeur d’une variable déclarée avec le mot clé var.

function mafonction () {
  'use strict'  
  var a = 10;
  a = a + 10; // 20
  var a = 3;
  console.warn(a);
}

Le mot clé let

Le mot clé let est un nouveau mot clé pour déclarer une variable. il va déclarer une variable dans le scope courant (global, function ou block)

Il est impossible de déclarer plusieurs fois la même variable Il est possible de réaffecter plusieurs fois la valeur d’une variable déclarée avec le mot clé let.

function mafonction () {
  'use strict'  
  let a = 10;
  a = a + 10; // 20
  let a = 3; // Uncaught SyntaxError: Identifier 'a' has already been declared
  console.warn(a);
}

Le mot clé const

Le mot clé const est un nouveau mot clé pour déclarer une variable. il va déclarer une variable dans le scope courant (global, function ou block)

Il est impossible de déclarer plusieurs fois la même variable Il est impossible de réaffecter plusieurs fois la valeur d’une variable déclarée avec le mot clé const.

Remarque : ce mot clé fut en fait crée pour déclarer des constantes. il sera forcement initialisé et affecté au même moment. Cependant attention bien que sa réaffectation ne soit pas possible, si votre variable est un objet, rien n’empêche la modification de ses propriétés !

const personne = {
	prenom: "romain",
  nom: "barraud"
}

personne = {prenom: "usurpateur", nom: "maléfique" } // Uncaught TypeError: Assignment to constant variable.

Cependant si on modifie la propriété prénom de personne alors il ne s’agit pas d’une affectation et ça fonctionne… Est-ce vraiment ce que l’on souhaitait pour notre variable ?

const personne = {
	prenom: "romain",
  nom: "barraud"
}
personne.prenom= "usurpateur";
personne.nom= "maléfique";
// :(

Je vous donne une astuce pour éviter cela et être sur d’avoir une constante même si la nature de votre variable est un objet vous pouvez utiliser la fonction freeze de Object pour geler votre variable si c’est un objet.

'use strict'
const personne = Object.freeze({
	prenom: "romain",
  nom: "barraud"
})
personne.prenom= "usurpateur"; //TypeError: Cannot assign to read only property 'prenom' of object '#<Object>'
personne.nom= "maléfique";

Qu'est-ce que le scope ?

La portée ou scope détermine la visibilité ou l'accessibilité d'une variable dans la zone de votre code.

Il existe en javascript 4 scopes différents :

  • Le global scope
  • Le function scope
  • Le block scope
  • Le scope lexical

Le global scope

Le scope global en javascript est la zone de plus haut niveau, c'est la zone en dehors de tout block et toute fonction.

Cela signifie que toute variable déclarée dans le scope global est accessible et modifiable dans les autres scopes.

Voici un exemple :

const variableGlobale = "toto"; // déclaration dans le scope global
  console.log(variableGlobale); // toto

  function salut() {
      // dans mon scope je peux accéder au scope global
      console.warn(`salut ${variableGlobale}`)
  }

  salut(); // salut toto

Remarque : L’inverse n’est pas vrai, c’est à dire qu’a partir du global scope il est impossible d’accéder aux variables des autres scopes (sauf var dans un block scope mais nous verrons ça par la suite).

Le function scope

Les variables déclarées dans des fonctions deviennent locales à cette fonction. Cela veut simplement dire que ces variables ne sont pas accessibles en dehors de cette fonction.

//scope global
function function1() {
    //scope local de function1
    function nestedFunction() {
        //scope local de nestedFunction
    }
}
//global scope
function function2() {
    //scope local de function2
}
//global scope

A partir du moment où vous déclarez une variable dans une fonction alors elle n'est visible est accessible uniquement dans cette fonction.

Il est alors impossible d'y accéder en dehors de cette fonction.

function shout() {
    // je suis dans le function scope de shout
    var sentence = "I HAVE A DREAM !";
    console.warn(sentence);
}
/* je suis dans le global scope,
   je ne peux pas accéder au function scope de shout
*/
console.warn(sentence); // Uncaught ReferenceError: sentence is not defined

Le block scope

Une portée de bloc est la zone à l'intérieur des des instructions conditionnelles if et switch ou encore des boucles for et while .

Dans l’implémentation ES2015 et supérieur, les mots-clés const et let permettent de déclarer des variables dans la portée d'un block, ce qui signifie que ces variables n'existent que dans le bloc correspondant.

function shoutShout() {

    if (true) {
        // Existe dans le scope de la function
        var varDeclaration = "LET IT ALL OUT";
        // Existe uniquement dans le block du if
        let letDeclaration = 'JUST A YELLOW LEMON TREE !';
        // Existe uniquement dans le block du if
        const constDeclaration = "COME ON BABY LET'S GO PARTY";
    }

    console.warn(varDeclaration); // LET IT ALL OUT
    console.warn(letDeclaration); // not defined
    console.warn(constDeclaration); // not defined
}

shoutShout();

Le scope lexical

Une portée lexicale en JavaScript signifie qu'une variable définie en dehors d'une fonction peut être accessible à l'intérieur d'une autre fonction définie après la déclaration de la variable.

const title = "Hello world";

(function sayHello() {
    console.warn(title); // Hello world
})();

Le scope et les variables

Pour le mot clé var

Le mot clé var va permettre de créer une variable dans le global scope ou le function scope dans lequel elle a été déclarée. Elle ne se limite pas au block scope.

function acheterGateau(amount) {
    if (amount > 3) {
        var hasCake = true;
        amount = 0;
    }
    console.log(hasCake); // Si j'ai assez d'argent alors j'ai le droit au gâteau
}

acheterGateau(10);

Pour le mot clé let ou const

function acheterGateau(amount) {
    if (amount > 3) {
        let hasCake = true;
        amount = 0;
    }
    console.log(hasCake); // hasCake is not defined
}

acheterGateau(10);

Les boucles avec let et var

// creation de 10 boutons
for (var i = 0; i < 10; i++) {
    let btn = document.createElement("button");
    btn.innerText = `button ${i}`;
    btn.id = `btn-${i}`
    document.body.appendChild(btn);
}

// pour chaque bouton on va associer un evenement onclick
for (var i = 0; i < 10; i++) {
    let btn = document.getElementById(`btn-${i}`);
    btn.addEventListener("click", function() {
    // each should log its value.
    console.log("Ma valeur est : " + i);
  });
}

Remarque: si on clique sur n’importe quel bouton on remarque que la valeur est toujours 10. Mais pourquoi ?


La boucle for va tout simplement créer la variable i dans son scope. Mais c’est un scope de type “block” et par conséquent cela signifie que la variable est déclarée avec le mot clé var est function scope. Elle est en dehors de la boucle for.

On peut vérifier cela il suffit de regarder si la variable existe

console.warn(i) // 10

Pour éviter cela, utiliser le mot clé let suffit, il va créer une variable nouvelle pour chaque tour de boucle.

// pour chaque bouton on va associer un evenement onclick
for (let i = 0; i < 10; i++) {
    let btn = document.getElementById(`btn-${i}`);
    btn.addEventListener("click", function() {
    // each should log its value.
    console.log("Ma valeur est : " + i);
  });
}

Le Hoisting

Toutes les variables déclarées en javascript sont Hoisted (hissées) par ce qui s’appelle le Hoisting (Hissage en français)

Ce Hoisting est un comportement par défaut de javascript qui va déplacer toutes les déclarations de votre code au plus haut de leur scope.

Cela signifie en théorie que vous pourriez affecter une valeur a une variable,

puis ensuite dans le même scope l’initialiser.

a = 3;
var a ;

Cela signifie que le code ci-dessus deviendra :

var a;
a = 3;

class, const et let sont également hissés, la différence est que ces derniers, ne vont pas être initialisé avec la valeur undefined lors du gats et donc cela provoque une erreur.

(function() {
    x; // undefined
    y; // Reference error: y is not defined

    var x = "local";
    let y = "local";
}());

Dans tous les cas gardez en mémoire qu’il vaut mieux en tant que bonne pratique javascript de déclarer toutes les variables au plus haut de votre scope.