En tant que développeur, j'ai souvent rencontré le terme « environnement lexical » mais je n'ai jamais vraiment pris le temps de l'explorer en profondeur. J'ai donc décidé d'approfondir et de documenter mes découvertes dans cet article - parce que "partager, c'est prendre soin ;)". À la fin de cet article, j'espère que nous aurons tous les deux une solide compréhension de ce qu'est un environnement lexical et que nous explorerons également ce qui se passe en mémoire, ce qu'est une structure de données et comment fonctionne la pile d'appels. Ne vous inquiétez pas, je vais rester simple et clair !
Avant de plonger dans les détails, permettez-moi de commencer par un bref aperçu. Ne vous inquiétez pas si certains concepts semblent complexes au début : je vais les décomposer et utiliser une analogie pour les rendre plus faciles à comprendre.
Un environnement lexical est une structure de données spéciale en JavaScript qui suit la portée des variables et des fonctions à un point spécifique du code.
Les structures de données sont des moyens d'organiser et de stocker des informations dans un ordinateur afin qu'elles puissent être utilisées efficacement. Les exemples courants incluent les tableaux, les objets, les listes et les arbres. Voir plus : Tutoriel sur les structures de données - GeeksforGeeks
Le terme « lexical » signifie que la portée et l'accessibilité des variables et des fonctions sont déterminées par l'endroit où elles sont écrites dans le code, plutôt que par la manière dont le programme s'exécute.
Rôles clés d'un environnement lexical :
Voici un exemple de code simple qui contient trois environnements lexicaux différents :
var sheep = 1; // Global sheep function drinkWater() { let waterTemperature = "cold"; console.log("The sheep is drinking " + waterTemperature + " water."); } var sheepHouseInArea = true; // Indicates whether the sheep house is present if (sheepHouseInArea) { let lambs = 2; // Lambs inside the house console.log("There are " + sheep + " sheep in the total area and " + lambs + " lambs!"); } // This will result in an error because 'lambs' // is only accessible inside the if block! console.log("How many lambs are there? " + lambs);
Les trois environnements lexicaux de ce code sont : la portée globale, la portée de la fonction drinkWater et la portée du bloc if. Pour rendre ces concepts plus faciles à comprendre, utilisons une analogie simple impliquant les moutons :
En marchant dehors cette semaine, j'ai croisé des moutons dans une zone clôturée et j'ai pensé : "Hé, c'est comme un environnement lexical !"
Laissez-moi vous expliquer : Imaginez une zone clôturée avec des moutons à l'intérieur. Les moutons ne peuvent faire des choses qu’à l’intérieur de la clôture, comme manger de l’herbe. Imaginez maintenant qu'il y ait une petite bergerie à l'intérieur de la clôture où les agneaux peuvent rester. Les agneaux à l'intérieur de la maison ne peuvent pas sortir, mais les moutons à l'extérieur peuvent entrer.
La clôture représente toute la zone où tout existe : les moutons, les agneaux, la maison et l'herbe. Cette zone clôturée est ce que nous appelons la portée globale. Dans cette zone clôturée, la bergerie est une section plus petite et séparée, représentant un bloc. Enfin, l'herbe que mange le mouton (yumyum) est comme une fonction dans le cadre global, une activité ou une action spécifique que le mouton peut accomplir dans cet espace.
Dans le bloc de code, la portée globale est représentée par la case rouge, la portée de la fonction drinkWater par la case bleue et la portée du bloc if par la case verte. Ce sont les trois environnements lexicaux.
Le mouton (représenté par var mouton = 1;) symbolise une variable dans le périmètre global, parcourant librement la zone clôturée. Il peut être utilisé aussi bien à l'extérieur qu'à l'intérieur de la fonction drinkWater et du bloc if.
La fonction DrinkWater représente une action que les moutons peuvent effectuer dans la zone clôturée. Nous pouvons appeler la fonction drinkWater depuis n’importe où dans le monde. Cependant, la fonction elle-même crée un nouvel environnement lexical lorsqu'elle est définie. À l'intérieur de cette fonction, les variables (comme let waterTemperature = 'cold';) ne sont accessibles qu'au sein de la fonction.
Le bloc if crée une nouvelle portée plus petite. Dans ce périmètre, représenté par la bergerie, il y a 2 agneaux (soit agneaux = 2). Dans cette portée, une instruction console.log enregistre la valeur de la variable lambs ainsi que la variable globale Sheep. La variable lambs est spécifique à la portée du bloc, tandis que la variable Sheep est extraite de l'environnement parent (la portée globale). Ceci est rendu possible par la référence d'environnement externe, qui permet à JavaScript de rechercher la chaîne de portée et de résoudre les variables introuvables dans l'environnement actuel.
La référence de l'environnement extérieur est une référence ou un pointeur dans un environnement lexical. Il pointe vers l'environnement lexical parent, permettant à JavaScript de résoudre les variables qui ne sont pas trouvées dans l'environnement actuel en recherchant la chaîne de portée.
Pouvez-vous modifier la fonction drinkWater() pour qu'elle enregistre le nombre total de moutons définis dans le périmètre global qui peuvent boire de l'eau ? Partagez votre réponse dans la section commentaires !
Nous voyons donc qu'il y a trois environnements lexicaux dans ce code : la portée globale, la portée de la fonction et la portée du bloc. Lorsqu’il existe plusieurs environnements lexicaux, nous parlons d’environnements lexicaux multiples. Il est important de comprendre que plusieurs environnements lexicaux peuvent exister dans un seul morceau de code. Chaque fois qu'une nouvelle portée est créée (par exemple, une fonction ou un bloc), un nouvel environnement lexical est généré, ce qui signifie que différentes parties de votre code peuvent avoir leurs propres environnements distincts.
Maintenant que nous comprenons le fonctionnement des environnements lexicaux, approfondissons le concept d'enregistrement d'environnement.
Chaque fois qu'un environnement lexical est créé - que ce soit pour la portée globale, une fonction ou un bloc - JavaScript génère automatiquement un enregistrement d'environnement pour celui-ci.
Cet enregistrement d'environnement est une structure de données qui assure le suivi de toutes les variables, fonctions et autres liaisons accessibles dans cette portée spécifique. Essentiellement, il sert de stockage interne pour tout ce qui est défini dans cet environnement, garantissant que les données correctes sont disponibles en cas de besoin pendant l'exécution du code.
La principale différence entre un environnement lexical et un enregistrement d'environnement :
Un environnement lexical est l'endroit où le code JavaScript s'exécute. Considérez-le comme le « cadre » ou le « contexte » dans lequel votre code existe. Ce contexte inclut la portée des variables et des fonctions, déterminant celles qui sont disponibles ou accessibles à tout moment dans le code. Par exemple, dans notre code, la variable lambs n'est accessible que dans l'environnement bordé de vert (la portée du bloc). Un environnement lexical comprend également une référence d'environnement externe (que nous avons déjà décrite), permettant d'accéder aux variables dans les environnements parents.
Un enregistrement d'environnement est une zone de stockage spécifique au sein d'un environnement lexical qui contient les variables réelles, les déclarations de fonction et d'autres identifiants utilisés dans cet environnement. Alors que l'environnement lexical constitue le contexte plus large, l'enregistrement d'environnement est l'endroit où les données du code, telles que les valeurs de variables et les définitions de fonctions, sont stockées. Chaque fois que JavaScript a besoin d'accéder à une variable ou à une fonction, il recherche dans l'enregistrement d'environnement de l'environnement lexical actuel.
Expliquons à nouveau l'environnement lexical et l'enregistrement d'environnement à l'aide de notre exemple de code :
Il existe trois environnements lexicaux, chacun avec son propre enregistrement d'environnement :
Ces déclarations sont accessibles dans tout le code. L'environnement global fait également référence à la fonction drinkWater, qui est définie dans cet environnement, et à l'instruction if, qui conduit à la création de sa propre portée de bloc lors de son exécution.
Portée de la fonction (drinkWater, boîte bleue), Environnement lexical 2 :
L'enregistrement d'environnement dans cet environnement contient la variable waterTemperature, déclarée à l'aide de let dans la fonction drinkWater. Cette variable n'est accessible qu'au sein de la fonction. Cependant, la fonction peut également accéder à des variables de l'environnement global comme le mouton.
Portée du bloc (si bloc, case verte), Environnement lexical 3 :
L'enregistrement d'environnement dans cet environnement contient la variable lambs, déclarée en utilisant let à l'intérieur du bloc if. Cette variable n'est accessible que dans la portée de ce bloc spécifique. Le bloc peut également accéder aux variables de son environnement parent, telles que Sheep et SheepHouseInArea.
Après avoir approfondi les environnements lexicaux et les enregistrements d'environnement, nous sommes maintenant prêts à comprendre comment JavaScript gère la mémoire et l'accès aux variables pendant l'exécution du code.
Lorsque votre code s'exécute, JavaScript crée un nouvel environnement lexical pour chaque fonction ou bloc de code. Chaque environnement possède son propre enregistrement d'environnement, stockant toutes les variables et fonctions définies dans cette portée. Cette configuration garantit une utilisation efficace de la mémoire, comme nous en avons discuté.
Behind the scenes, the JavaScript engine handles these lexical environments in memory. The call stack is used for tracking function calls, while block scopes create new lexical environments linked to their outer environments. However, unlike functions, these block scopes aren't pushed onto the call stack.
The call stack is a fundamental concept in how Javascript executes code.
The call stack is a data structure that keeps track of function calls in a program. It works on a Last-In-First-Out (LIFO) principle. Here's how it works:
Key points about the call stack:
Now you know why its called stack overflow haha!
Here's a simple example to illustrate:
function greet(name) { console.log('Hello, ' + name); } function processUser(user) { greet(user); } processUser('Alice');
As each function completes, it's popped off the stack until we return to the global context.
In our sheep code example, can you identify if anything is placed on the call stack during execution? Share your thoughts in the comments section!
That's it for Part 1! I hope this post has helped you gain a solid understanding of how JavaScript handles lexical environments, environment records, and what happens behind the scenes in memory. I've learned a lot in the process, and I hope you have too. If you have any questions or feedback, I'd love to hear from you - let's learn and improve together!
I titled this post 'Part 1' because I plan to follow up with 'Part 2,' where I'll dive into three major concepts that are closely linked to lexical environments:
These concepts are super important because they directly impact how your JavaScript code behaves. Understanding them will help you write cleaner, more efficient code and avoid some common pitfalls.
Stay tuned!
--
Please also follow me on my Medium: https://medium.com/@ensing89
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!