Heim > Web-Frontend > js-Tutorial > JS – Lexikalische Umgebungen in JavaScript verstehen – Deep Dive – Teil 1

JS – Lexikalische Umgebungen in JavaScript verstehen – Deep Dive – Teil 1

王林
Freigeben: 2024-08-30 18:38:31
Original
389 Leute haben es durchsucht

As a developer, I have often encountered the term "lexical environment" but I never really took the time to fully explore it in depth. So, I decided to dive deep and document my findings in this post - because "sharing is caring ;)". By the end of this post, I hope we will both have a solid understanding of what a lexical environment is and we will also explore what happens in memory, what a data structure is, and how the call stack works. Don't worry - I'll keep it simple and clear!

The Lexical Environment

Before diving into the details, let me start with a brief overview. Don't worry if some concepts seem complex at first - I'll break them down and use an analogy to make them easier to understand.

A lexical environment is a special data structure in JavaScript that tracks the scope of variables and functions at a specific point in the code.

Data structures are ways to organize and store information in a computer so it can be used efficiently. Common examples include arrays, objects, lists, and trees. See more: Data Structures Tutorial - GeeksforGeeks

The term "lexical" means that the scope and accessibility of variables and functions are determined by where they are written in the code, rather than how the program runs.

Key roles of a lexical environment:

  • It stores all variables and function declarations within a specific scope (such as a function or a block).
  • It makes these stored items accessible at that particular point in your code.

Here's a simple code example that contains three different lexical environments:

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);
Nach dem Login kopieren

The three lexical environments in this code are: the global scope, the drinkWater function scope, and the if block scope. To make these concepts easier to grasp, let's use a simple analogy involving sheep:

The Sheep Analogy:

While walking outside this week, I came across some sheep inside a fenced area and thought, "Hey, this is like a lexical environment!"

Let me explain: Imagine a fenced area with sheep inside. The sheep can only do things within the fence, like eating grass. Now, imagine there's a small sheep house inside the fence where lambs can stay. The lambs inside the house can't go outside, but the sheep outside can go in.

JS — Understanding Lexical Environments in JavaScript — Deep Dive — Part 1

Breaking Down the Analogy:

The fence represents the entire area where everything exists - the sheep, lambs, house, and grass. This fenced area is what we refer to as the global scope. Within this fenced area, the sheep house is a smaller, separate section, representing a block scope. Finally, the grass which the sheep eat (yumyum) is like a function within the global scope, a specific activity or action that the sheep can perform within that space.

In the code block, the global scope is represented by the red box, the drinkWater function scope by the blue box, and the if block scope by the green box. These are the three lexical environments.

JS — Understanding Lexical Environments in JavaScript — Deep Dive — Part 1

Global Scope (Fenced Area):

The sheep (represented by var sheep = 1;) symbolizes a variable in the global scope, freely roaming the fenced area. It can be used both outside and inside the drinkWater function and the if block.

Function Scope (drinkWater):

The drinkWater function represents an action the sheep can perform within the fenced area. We can call the drinkWater function from anywhere in the global scope. However, the function itself creates a new lexical environment when it's defined. Inside this function, variables (like let waterTemperature = 'cold';) are only accessible within the function.

Block Scope (if block):

The if block creates a new, smaller scope. In this scope, represented by the sheep house, there are 2 lambs (let lambs = 2). Inside this scope, a console.log statement logs the value of the lambs variable as well as the global sheep variable. The lambs variable is specific to the block scope, while the sheep variable is fetched from the parent environment (the global scope). This is made possible by the Outer Environment Reference, which allows JavaScript to look up the scope chain and resolve variables not found in the current environment.

The Outer Environment Reference is a reference or a pointer within a lexical environment. It points to the parent lexical environment, allowing JavaScript to resolve variables that aren't found in the current environment by looking up the scope chain.

Fragestunde!

Können Sie die Funktion drinkWater() so ändern, dass sie die Gesamtzahl der im globalen Bereich definierten Schafe protokolliert, die das Wasser trinken können? Teilen Sie Ihre Antwort im Kommentarbereich!

Mehrere lexikalische Umgebungen verstehen

Wir sehen also, dass es in diesem Code drei lexikalische Umgebungen gibt: den globalen Bereich, den Funktionsbereich und den Blockbereich. Wenn es mehr als eine lexikalische Umgebung gibt, nennen wir es mehrere lexikalische Umgebungen. Es ist wichtig zu verstehen, dass in einem einzigen Codeabschnitt mehrere lexikalische Umgebungen vorhanden sein können. Jedes Mal, wenn ein neuer Bereich erstellt wird (z. B. eine Funktion oder ein Block), wird eine neue lexikalische Umgebung generiert, was bedeutet, dass verschiedene Teile Ihres Codes ihre eigenen separaten Umgebungen haben können.

Der Umweltbericht

Da wir nun verstehen, wie lexikalische Umgebungen funktionieren, wollen wir tiefer in das Konzept des Umgebungsdatensatzes eintauchen.

Immer wenn eine lexikalische Umgebung erstellt wird – sei es für den globalen Bereich, eine Funktion oder einen Block – generiert JavaScript automatisch einen Umgebungseintrag dafür.

Dieser Umgebungsdatensatz ist eine Datenstruktur, die alle Variablen, Funktionen und anderen Bindungen verfolgt, auf die innerhalb dieses spezifischen Bereichs zugegriffen werden kann. Im Wesentlichen fungiert es als interner Speicher für alles, was in dieser Umgebung definiert ist, und stellt sicher, dass bei Bedarf während der Codeausführung die richtigen Daten verfügbar sind.

Unterschied zwischen lexikalischer Umgebung und Umgebungsdatensatz

Der Hauptunterschied zwischen einer lexikalischen Umgebung und einem Umgebungsdatensatz:

Eine lexikalische Umgebung ist der Ort, an dem JavaScript-Code ausgeführt wird. Betrachten Sie es als die „Einstellung“ oder den „Kontext“, in dem Ihr Code existiert. Dieser Kontext umfasst den Umfang von Variablen und Funktionen und bestimmt, welche an jeder Stelle im Code verfügbar oder zugänglich sind. In unserem Code ist die Lambs-Variable beispielsweise nur innerhalb der grün umrandeten Umgebung (dem Blockbereich) zugänglich. Eine lexikalische Umgebung umfasst auch eine äußere Umgebungsreferenz (die wir bereits beschrieben haben), die den Zugriff auf Variablen in übergeordneten Umgebungen ermöglicht.

Ein Umgebungsdatensatz ist ein bestimmter Speicherbereich innerhalb einer lexikalischen Umgebung, der die tatsächlichen Variablen, Funktionsdeklarationen und andere in dieser Umgebung verwendete Bezeichner enthält. Während die lexikalische Umgebung den breiteren Kontext darstellt, werden im Umgebungsdatensatz die Daten des Codes – wie Variablenwerte und Funktionsdefinitionen – gespeichert. Wann immer JavaScript auf eine Variable oder Funktion zugreifen muss, sucht es im Umgebungsdatensatz der aktuellen lexikalischen Umgebung.

Lassen Sie uns die lexikalische Umgebung und den Umgebungsdatensatz noch einmal anhand unseres Codebeispiels erklären:

JS — Understanding Lexical Environments in JavaScript — Deep Dive — Part 1

Es gibt drei lexikalische Umgebungen, jede mit ihrem eigenen Umgebungseintrag:

  1. Globaler Geltungsbereich (rotes Kästchen), Lexikalische Umgebung 1: Diese lexikalische Umgebung wird auf globaler Ebene erstellt. Der Umgebungsdatensatz innerhalb dieser Umgebung enthält:
    • Das variable Schaf.
    • Die Funktion drinkWater.
    • Die Variable SheepHouseInArea.

Auf diese Deklarationen kann im gesamten Code zugegriffen werden. Die globale Umgebung referenziert auch die Funktion drinkWater, die in dieser Umgebung definiert ist, und die if-Anweisung, die bei Ausführung zur Erstellung eines eigenen Blockbereichs führt.

  1. Funktionsbereich (drinkWater, blaue Box), Lexikalische Umgebung 2:
    Der Umgebungsdatensatz in dieser Umgebung enthält die Variable „waterTemperature“, die mithilfe von „let“ in der Funktion „drinkWater“ deklariert wird. Auf diese Variable kann nur innerhalb der Funktion zugegriffen werden. Die Funktion kann jedoch auch auf Variablen in der globalen Umgebung wie Schafe zugreifen.

  2. Blockbereich (wenn Block, grünes Kästchen), Lexikalische Umgebung 3:
    Der Umgebungseintrag in dieser Umgebung enthält die Variable lambs, die mit let im if-Block deklariert wird. Auf diese Variable kann nur innerhalb dieses spezifischen Blockbereichs zugegriffen werden. Der Block kann auch auf Variablen aus seiner übergeordneten Umgebung zugreifen, wie z. B. Sheep und SheepHouseInArea.

Was hinter den Kulissen in der Erinnerung passiert

Nachdem wir uns eingehend mit lexikalischen Umgebungen und Umgebungsdatensätzen befasst haben, sind wir nun bereit zu verstehen, wie JavaScript den Speicher und den Variablenzugriff während der Codeausführung verwaltet.

Wenn Ihr Code ausgeführt wird, erstellt JavaScript eine neue lexikalische Umgebung für jede Funktion oder jeden Codeblock. Jede Umgebung verfügt über einen eigenen Umgebungsdatensatz, in dem alle in diesem Bereich definierten Variablen und Funktionen gespeichert werden. Dieses Setup gewährleistet eine effiziente Speichernutzung, wie wir besprochen haben.

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.

What is 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:

  • When a function is called, it's added (pushed) to the top of the stack.
  • When a function finishes executing, it's removed (popped) from the top of the stack.
  • The stack also keeps track of the current position in the code.

Key points about the call stack:

  • It records where in the program we are.
  • If we step into a function, we put it on the top of the stack.
  • If we return from a function, we pop it off the stack.
  • The stack has a maximum size, and if that limit is exceeded, it results in a "stack overflow" error.

JS — Understanding Lexical Environments in JavaScript — Deep Dive — Part 1

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');
Nach dem Login kopieren
  1. main() (global execution context)
  2. processUser('Alice')
  3. greet('Alice')

As each function completes, it's popped off the stack until we return to the global context.

The last final question!

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!


Conclusion

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:

  1. Closures: Think of them as magical boxes that let functions remember and access variables from their outer environment, even after the outer function has finished executing.
  2. Scope Chains: We'll explore how JavaScript navigates through nested environments to find variables, like a treasure hunt in your code.
  3. Hoisting: This explains why some variables and functions seem to "float" to the top of their scope, which can be tricky to understand but is crucial for writing predictable code.

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

Das obige ist der detaillierte Inhalt vonJS – Lexikalische Umgebungen in JavaScript verstehen – Deep Dive – Teil 1. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage