104 votes

Fonctionnement de la portée des variables et du levage en JavaScript

J'ai juste lu un excellent article sur JavaScript Scoping and Hoisting par Ben Cherry dans lequel il donne l'exemple suivant:

var a = 1;

function b() {
    a = 10;
    return;

    function a() {}
}
b();
alert(a);

En utilisant le code ci-dessus, le navigateur affichera "1".

Je ne suis toujours pas sûr pourquoi cela retourne "1". Certaines des choses qu'il dit me viennent à l'esprit comme: Toutes les déclarations de fonction sont hissées en haut. Vous pouvez restreindre la portée d'une variable en utilisant une fonction. Ça ne clique toujours pas pour moi.

134voto

Peter Olson Points 30452

Le levage des fonctions signifie que les fonctions sont déplacées en haut de leur portée. C'est-à-dire,

function b() {  
   a = 10;  
   return;  
   function a() {} 
} 

sera réécrit par l'interpréteur comme ceci

function b() {
  function a() {}
  a = 10;
  return;
}

Bizarre, non?

Aussi, dans cet exemple,

function a() {}

se comportait de la même manière que

var a = function () {};

Donc, en essence, voici ce que fait le code:

var a = 1;                 //définit "a" dans la portée globale
function b() {  
   var a = function () {}; //définit "a" dans la portée locale
   a = 10;                 //écrase la variable locale "a"
   return;      
}       
b();       
alert(a);                 //affiche la variable globale "a"

6voto

Kevin Points 57797

Ce que vous devez retenir, c'est qu'il analyse la fonction entière et résout toutes les déclarations de variables avant de l'exécuter. Donc....

fonction a() {} 

devient vraiment

var a = function () {}

var a le force dans une portée locale, et la portée de la variable est à travers toute la fonction, donc la variable globale a reste à 1 car vous avez déclaré a dans une portée locale en en faisant une fonction.

5voto

Digital Plane Points 11741

La fonction a est hissée à l'intérieur de la fonction b :

var a = 1; 
function b() { 
   function a() {} 
   a = 10; 
   return;
} 
b(); 
alert(a);

ce qui est presque comme utiliser var:

var a = 1; 
function b() { 
   var a = function () {};
   a = 10; 
   return;
} 
b(); 
alert(a);

La fonction est déclarée localement, et le réglage de a se fait uniquement dans la portée locale, pas dans le global.

3voto

KhanSharp Points 1754
  1. La déclaration de fonction function a(){} est hissée en premier et se comporte comme var a = function () {};, donc en local, a est créé.
  2. Si vous avez deux variables avec le même nom (une globale et une locale), la variable locale a toujours la priorité sur la variable globale.
  3. Lorsque vous définissez a=10, vous définissez la variable locale a, pas la variable globale.

Par conséquent, la valeur de la variable globale reste la même et vous obtenez l'alerte 1

2voto

Donato Points 1745

Étonnamment, aucun des réponses ici ne mentionne la pertinence du Contexte d'Exécution dans la Chaîne de Portée.

Le Moteur JavaScript enveloppe le code actuellement en cours d'exécution dans un Contexte d'Exécution. Le contexte d'exécution de base est le Contexte d'Exécution global. Chaque fois qu'une nouvelle fonction est invoquée, un nouveau Contexte d'Exécution est créé et placé sur la Pile d'Exécution. Pensez à un Cadre de Pile assis sur une Pile d'Invocation dans d'autres langages de programmation. Le dernier entré est le premier sorti. Maintenant, chaque Contexte d'Exécution a son propre Environnement de Variables et Environnement Externe en JavaScript.

Je vais utiliser l'exemple ci-dessous comme démonstration.

1) Tout d'abord, nous entrons dans la Phase de Création du Contexte d'Exécution global. L'Environnement Externe et l'Environnement de Variables de l'Environnement Lexical sont créés. L'Objet Global est configuré et placé en mémoire avec la variable spéciale 'this' pointant vers celui-ci. La fonction a et son code et la variable myVar avec une valeur indéfinie sont placés en mémoire dans l'Environnement de Variables global. Il est important de noter que le code de la fonction a n'est pas exécuté. Il est simplement placé en mémoire avec la fonction a.

2) Ensuite, c'est la Phase d'Exécution du Contexte d'Exécution. myVar n'est plus une valeur indéfinie. Il est initialisé avec une valeur de 1, qui est stockée dans l'Environnement de Variables global. La fonction a est invoquée et un nouveau Contexte d'Exécution est créé.

3) Dans le Contexte d'Exécution de la fonction a, il passe par la Phase de Création et d'Exécution de son propre Contexte d'Exécution. Il a son propre Environnement Externe et Environnement de Variables, et donc son propre Environnement Lexical. La fonction b et la variable myVar sont stockées dans son Environnement de Variables. Cet Environnement de Variables est distinct de l'Environnement de Variables global. Comme la fonction a se trouve lexicalement (physiquement dans le code) sur le même niveau que le Contexte d'Exécution global, son Environnement Externe est le Contexte d'Exécution global. Ainsi, si la fonction a devait faire référence à une variable qui n'est pas dans son Environnement de Variables, elle chercherait dans la Chaîne de Portée et essaierait de trouver la variable dans l'Environnement de Variables du Contexte d'Exécution global.

4) La fonction b est invoquée dans la fonction a. Un nouveau Contexte d'Exécution est créé. Comme il se trouve lexicalement dans la fonction a, son Environnement Externe est a. Donc, lorsque elle fait référence à myVar, comme myVar n'est pas dans l'Environnement de Variables de la fonction b, elle cherchera dans l'Environnement de Variables de la fonction a. Elle le trouve là et console.log affiche 2. Mais si la variable n'était pas dans l'Environnement de Variables de la fonction a, alors puisque l'Environnement Externe de la fonction a est le Contexte d'Exécution global, alors la Chaîne de Portée continuera à chercher là.

5) Après l'exécution des fonctions b et a, elles sont retirées de la Pile d'Exécution. Le Moteur JavaScript à un seul thread continue l'exécution dans le Contexte d'Exécution global. Il invoque la fonction b. Mais il n'y a pas de fonction b dans l'Environnement de Variables global et il n'y a pas d'autre Environnement Externe à rechercher dans le Contexte d'Exécution global. Ainsi, une exception est levée par le Moteur JavaScript.

function a(){
  function b(){
    console.log(myVar);
  }

  var myVar = 2;
  b();
}

var myVar = 1;
a();
b();
> 2
> Uncaught ReferenceError: b is not defined

L'exemple ci-dessous montre la Chaîne de Portée en action. Dans l'Environnement de Variables du Contexte d'Exécution de la fonction b, il n'y a pas de myVar. Elle recherche donc dans son Environnement Externe, qui est la fonction a. La fonction a n'a pas non plus myVar dans son Environnement de Variables. Donc le Moteur recherche dans l'Environnement Externe de la fonction a, qui est l'Environnement Externe du Contexte d'Exécution global et myVar y est défini. Ainsi, le console.log affiche 1.

function a(){
  function b(){
    console.log(myVar);
  }

  b();
}

var myVar = 1;
a();
> 1

En ce qui concerne le Contexte d'Exécution et l'Environnement Lexical qui lui est associé, y compris l'Environnement Externe et l'Environnement de Variables, permettent le cloisonnement des variables en JavaScript. Même si vous invoquez la même fonction plusieurs fois, pour chaque invocation, elle créera son propre Contexte d'Exécution. Ainsi, chaque Contexte d'Exécution aura sa propre copie des variables dans son Environnement de Variables. Il n'y a pas de partage de variables.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X