1
votes

Levage dans ES6 par rapport à "let" et à bloquer la portée

Lorsque j'exécute le code suivant écrit à l'aide de ES6, let :

function doSmth(){
  age=27;
}
let age;
doSmth();
console.log(age);

J'obtiens la sortie correctement comme 27.

Maintenant, je suis un peu confus quant à la façon dont cela fonctionne. Plus précisément, j'ai lu que let a une portée de bloc.

Alors, comment se fait-il que let age , qui est déclaré en dehors de la fonction doSmth , est la même variable à l'intérieur de la fonction doSmth ? Est-ce quelque chose à voir avec la portée globale, mais si oui, quel rôle a exactement la portée de bloc?


6 commentaires

doSmth fonction doSmth et l' age sont tous deux dans le même bloc, donc la variable age est toujours dans la portée.


doSmth est dans la même portée que age .


voir ici comment l'exécution du code


@ jagad89 - Même si vous l'enveloppez dans un bloc, ce code fonctionnera. Cela créera simplement un autre âge variable à l'échelle mondiale.


Je pense que c'est lié à la closure , la fonction doSmth a l'environnement de contexte dont l' age variable age extérieur.


@Vatsal est d'accord avec vous.


4 Réponses :


3
votes

"Let has block scope" ne signifie pas que la variable doit être définie dans le même bloc - cela signifie que le niveau auquel la variable est définie peut être un bloc.

En revanche, le niveau auquel une variable var est définie ne peut pas être un bloc, sauf si ce bloc est également un bloc fonction. Si une variable var est initialisée à l'intérieur d'un bloc non fonctionnel, elle sera hissée vers le bloc externe le plus proche qui est également un bloc fonction.

Peu importe si une variable est déclarée avec var , let ou const , elle sera référençable n'importe où ailleurs dans le même bloc dans lequel elle a été déclarée, y compris à l'intérieur des blocs enfants. (Pour let et const , la ligne qui déclare la variable, par exemple const foo = , doit également être exécutée pour qu'une référence à foo fonctionne sans lancer.)

Une autre façon de le voir est: si une variable est référencée quelque part, l'interpréteur cherche si elle est initialisée dans le bloc courant . Si tel est le cas, c'est la liaison utilisée. Sinon, il regarde dans le bloc externe suivant pour voir si la variable y est initialisée - si c'est le cas, c'est la liaison qui est utilisée. Etc. S'il atteint le niveau supérieur et qu'aucune liaison n'a été trouvée et que ce n'est pas une propriété de l'objet global, cette référence lèvera une ReferenceError .

Dans votre code, la variable age se trouve être globale, puisqu'elle est définie au niveau supérieur, mais cela n'a pas vraiment d'importance - le code fonctionnerait de la même manière si l' age n'était qu'un bloc externe , mais pas au niveau supérieur, par exemple:

function foo() {
  function doSmth() {
    age = 27;
  }
  let age;
  doSmth();
  console.log(age);
}

foo();


6 commentaires

Votre troisième paragraphe n'est pas entièrement vrai. Vous ne pouvez pas référencer une variable let ou const dans le bloc AVANT sa définition.


Le commentaire mentionné ci-dessus par @ jfriend00 est-il vrai?


@testndtv Oui - voir (Pour let et const, la ligne qui initialise la variable, par exemple const foo = , doit également être exécutée pour qu'une référence à foo fonctionne sans lancer.)


Aussi, pour mieux comprendre cela, il faut garder à l'esprit que la chaîne de portées est toujours applicable et se comporte de la même manière pour "let" que pour "var" ... comme dans l'exemple ci-dessus, une chaîne de portées / portée lexicale est également à venir en jeu, c'est pourquoi nous obtenons l'âge de 27 ans (c'est-à-dire la valeur de référence du parent / contenant la portée). Cela serait-il correct?


@testndtv Oui, et cela fonctionnerait exactement de la même manière si age était déclaré avec var place.


Souhaitez-vous corriger cette instruction en "déclare la variable" au lieu de "initialise la variable"? "Pour let et const, la ligne qui initialise la variable, par exemple const foo =, doit également avoir exécuté pour qu'une référence à foo fonctionne sans lancer."



0
votes

Dans votre code, doSmth et age ont une portée globale. Windows pour navigateur et objet module pour nœud. Donc, ils sont fondamentalement dans la même portée.


2 commentaires

Ce n'est pas la portée globale de node.js. Cela peut être la portée du module. Vous ne pouvez pas écrire de code dans la portée globale dans node.js.


@ jfriend00 - Corrigé :)



1
votes

Maintenant, je suis un peu confus quant à la façon dont cela fonctionne. Plus précisément, j'ai lu que let a une portée de bloc.

Oui, let a une portée de bloc. Mais un bloc tel que l'intérieur de votre fonction doSmmth() a accès à TOUTES les variables des portées parent qui ont été définies ou hissées au moment de l'exécution de la fonction.

Alors, let age; est dans la portée parent et a été défini au moment où doSmth() s'exécute. Par conséquent, l'interpréteur chargé de rechercher une variable nommée age trouve très bien dans la portée parent.

Alors, comment se fait-il que let age , qui est déclaré en dehors de la fonction doSmth , est la même variable à l'intérieur de la fonction doSmth?

Voici comment l'interprète fonctionne à cet égard. Votre code s'exécute dans ces étapes:

  1. Analysez le code que vous avez présenté.
  2. La function doSmth() est rencontrée. Les fonctions sont hissées en haut de la portée de la fonction conteneur afin que la définition soit immédiatement ajoutée à cet objet de portée. let age est également rencontré dans l'analyse et est ajouté à la portée analysée, mais il est marqué d'une manière qu'il ne sera pas encore accessible au code.
  3. Commencez à exécuter le code. Un nouvel objet d'étendue est créé à partir du code analysé.
  4. let age; est rencontré. Cela marque la variable d' age qui était précédemment placée dans cette portée au moment de l'analyse comme maintenant disponible pour le code à utiliser.
  5. Appelez doSmth() . Cela pousse une adresse de retour sur la pile d'appels et lors de la configuration pour exécuter doSmth() , il crée une nouvelle portée de fonction.
  6. Il exécute ensuite age = 27 intérieur de cette fonction. L'interpréteur recherche dans la portée locale un symbole nommé age . Il n'en trouve pas. Ainsi, il remonte la chaîne de portée et regarde dans la portée parent. Il trouve un symbole correspondant et l'utilise.
  7. La fonction retourne qui fait sortir une adresse de retour de la pile d'appels et réinitialise la portée actuelle à la portée parente et l'exécution se poursuit juste après où doSmth() était et exécute le console.log(age) .
  8. L'interpréteur recherche dans la portée locale un symbole nommé age , le trouve et l'utilise.

Donc, les principales clés ici qui semblent vous confondre sont:

  1. Si un symbole n'est pas trouvé dans l'étendue locale, les étendues parentes sont recherchées pour le nom du symbole.

  2. let crée un symbole à portée de bloc, mais il est toujours accessible par toutes les fonctions enfants également déclarées dans cette portée.

  3. Dans ce code particulier, il n'y aurait aucune différence entre let et var et ce n'est pas le levage qui fait que cela fonctionne. Ce sont les portées parentales.


0 commentaires

0
votes

Votre code passera par deux phases.

  1. Compilation où tout le levage se produira (var variables déclarées).
  2. Exécution où l'exécution réelle du code se produira.

Dans la première phase, vous déclarerez la fonction doSmith et la variable d' age . Avant la phase d'exécution, le moteur JS sait ce qu'est la variable age et ce qu'est doSmith .

Dans la phase suivante, c'est-à-dire la phase d'exécution, l'exécution aura lieu,

doSmth();
console.log(age);

sera exécuté. Par conséquent, la variable d'âge est accessible à la fonction doSmith en phase d'exécution.


1 commentaires

@Vatsal Oui même livre