”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > JS — 理解 JavaScript 中的词法环境 — 深入探究 — 第 1 部分

JS — 理解 JavaScript 中的词法环境 — 深入探究 — 第 1 部分

发布于2024-11-08
浏览:253

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);

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.

Question Time!

Can you modify the drinkWater() function so that it logs the total number of sheep defined in the global scope that can drink the water? Share your answer in the comments section!

Understanding Multiple Lexical Environments

So, we see that there are three lexical environments in this code: the global scope, the function scope, and the block scope. When there is more than one lexical environment, we call it multiple lexical environments. It's important to understand that multiple lexical environments can exist in a single piece of code. Each time a new scope is created (e.g., a function or a block), a new lexical environment is generated, meaning different parts of your code can have their own separate environments.

The Environment Record

Now that we understand how lexical environments work, let's dive deeper into the concept of the environment record.

Whenever a lexical environment is created - whether it's for the global scope, a function, or a block - JavaScript automatically generates an environment record for it.

This environment record is a data structure that keeps track of all the variables, functions, and other bindings that are accessible within that specific scope. Essentially, it acts as the internal storage for everything defined within that environment, ensuring that the correct data is available when needed during code execution.

Difference Between Lexical Environment and Environment Record

The key difference between a lexical environment and an environment record:

A lexical environment is the place where JavaScript code runs. Think of it as the "setting" or "context" in which your code exists. This context includes the scope of variables and functions, determining which ones are available or accessible at any point in the code. For example, in our code, the lambs variable is only accessible within the green-bordered environment (the block scope). A lexical environment also includes an Outer Environment Reference (which we already described), allowing access to variables in parent environments.

An environment record is a specific storage area within a lexical environment that holds the actual variables, function declarations, and other identifiers used in that environment. While the lexical environment is the broader context, the environment record is where the code's data - like variable values and function definitions - is stored. Whenever JavaScript needs to access a variable or function, it looks in the environment record of the current lexical environment.

Let's explain the lexical environment and environment record again using our code example:

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

There are three lexical environments, each with its own environment record:

  1. Global Scope (red box), Lexical Environment 1: This lexical environment is created at the global level. The environment record within this environment contains:
    • The variable sheep.
    • The function drinkWater.
    • The variable sheepHouseInArea.

These declarations are accessible throughout the entire code. The global environment also references the function drinkWater, which is defined in this environment, and the if statement, which leads to the creation of its own block scope when executed.

  1. Function Scope (drinkWater, blue box), Lexical Environment 2:
    The environment record in this environment contains the variable waterTemperature, declared using let inside the drinkWater function. This variable is only accessible within the function. However, the function can also access variables in the global environment like sheep.

  2. Block Scope (if block, green box), Lexical Environment 3:
    The environment record within this environment contains the variable lambs, declared using let inside the if block. This variable is only accessible within this specific block scope. The block can also access variables from its parent environment, such as sheep and sheepHouseInArea.

What Happens Behind the Scenes in Memory

After diving deep into lexical environments and environment records, we're now ready to understand how JavaScript manages memory and variable access during code execution.

When your code runs, JavaScript creates a new lexical environment for each function or block of code. Each environment has its own environment record, storing all the variables and functions defined in that scope. This setup ensures efficient memory usage, as we've discussed.

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');
  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

版本声明 本文转载于:https://dev.to/nuri/js-understanding-lexical-environments-in-javascript-deep-dive-part-1-417a?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 同实例无需转储复制MySQL数据库方法
    同实例无需转储复制MySQL数据库方法
    在同一实例上复制一个MySQL数据库而无需转储在同一mySQL实例上复制数据库,而无需创建InterMediate sqql script。以下方法为传统的转储和IMPORT过程提供了更简单的替代方法。 直接管道数据 MySQL手动概述了一种允许将mysqldump直接输出到MySQL clie...
    编程 发布于2025-07-06
  • 如何使用PHP从XML文件中有效地检索属性值?
    如何使用PHP从XML文件中有效地检索属性值?
    从php $xml = simplexml_load_file($file); foreach ($xml->Var[0]->attributes() as $attributeName => $attributeValue) { echo $attributeName,...
    编程 发布于2025-07-06
  • 如何正确使用与PDO参数的查询一样?
    如何正确使用与PDO参数的查询一样?
    在pdo 中使用类似QUERIES在PDO中的Queries时,您可能会遇到类似疑问中描述的问题:此查询也可能不会返回结果,即使$ var1和$ var2包含有效的搜索词。错误在于不正确包含%符号。通过将变量包含在$ params数组中的%符号中,您确保将%字符正确替换到查询中。没有此修改,PDO...
    编程 发布于2025-07-06
  • 如何处理PHP文件系统功能中的UTF-8文件名?
    如何处理PHP文件系统功能中的UTF-8文件名?
    在PHP的Filesystem functions中处理UTF-8 FileNames 在使用PHP的MKDIR函数中含有UTF-8字符的文件很多flusf-8字符时,您可能会在Windows Explorer中遇到comploreer grounder grounder grounder gro...
    编程 发布于2025-07-06
  • Async Void vs. Async Task在ASP.NET中:为什么Async Void方法有时会抛出异常?
    Async Void vs. Async Task在ASP.NET中:为什么Async Void方法有时会抛出异常?
    在ASP.NET async void void async void void void void void的设计无需返回asynchroncon而无需返回任务对象。他们在执行过程中增加未偿还操作的计数,并在完成后减少。在某些情况下,这种行为可能是有益的,例如未期望或明确预期操作结果的火灾和...
    编程 发布于2025-07-06
  • 将图片浮动到底部右侧并环绕文字的技巧
    将图片浮动到底部右侧并环绕文字的技巧
    在Web设计中围绕在Web设计中,有时可以将图像浮动到页面右下角,从而使文本围绕它缠绕。这可以在有效地展示图像的同时创建一个吸引人的视觉效果。 css位置在右下角,使用css float and clear properties: img { 浮点:对; ...
    编程 发布于2025-07-06
  • 如何使用替换指令在GO MOD中解析模块路径差异?
    如何使用替换指令在GO MOD中解析模块路径差异?
    在使用GO MOD时,在GO MOD 中克服模块路径差异时,可能会遇到冲突,其中3个Party Package将另一个PAXPANCE带有导入式套件之间的另一个软件包,并在导入式套件之间导入另一个软件包。如回声消息所证明的那样: go.etcd.io/bbolt [&&&&&&&&&&&&&&&&...
    编程 发布于2025-07-06
  • 为什么使用固定定位时,为什么具有100%网格板柱的网格超越身体?
    为什么使用固定定位时,为什么具有100%网格板柱的网格超越身体?
    网格超过身体,用100%grid-template-columns 为什么在grid-template-colms中具有100%的显示器,当位置设置为设置的位置时,grid-template-colly修复了?问题: 考虑以下CSS和html: class =“ snippet-code”> g...
    编程 发布于2025-07-06
  • 编译器报错“usr/bin/ld: cannot find -l”解决方法
    编译器报错“usr/bin/ld: cannot find -l”解决方法
    错误:“ usr/bin/ld:找不到-l “ 此错误表明链接器在链接您的可执行文件时无法找到指定的库。为了解决此问题,我们将深入研究如何指定库路径并将链接引导到正确位置的详细信息。添加库搜索路径的一个可能的原因是,此错误是您的makefile中缺少库搜索路径。要解决它,您可以在链接器命令中添加...
    编程 发布于2025-07-06
  • 我可以将加密从McRypt迁移到OpenSSL,并使用OpenSSL迁移MCRYPT加密数据?
    我可以将加密从McRypt迁移到OpenSSL,并使用OpenSSL迁移MCRYPT加密数据?
    将我的加密库从mcrypt升级到openssl 问题:是否可以将我的加密库从McRypt升级到OpenSSL?如果是这样,如何?答案:是的,可以将您的Encryption库从McRypt升级到OpenSSL。可以使用openssl。附加说明: [openssl_decrypt()函数要求iv参...
    编程 发布于2025-07-06
  • 如何使用组在MySQL中旋转数据?
    如何使用组在MySQL中旋转数据?
    在关系数据库中使用mySQL组使用mySQL组进行查询结果,在关系数据库中使用MySQL组,转移数据的数据是指重新排列的行和列的重排以增强数据可视化。在这里,我们面对一个共同的挑战:使用组的组将数据从基于行的基于列的转换为基于列。 Let's consider the following ...
    编程 发布于2025-07-06
  • 如何使用Java.net.urlConnection和Multipart/form-data编码使用其他参数上传文件?
    如何使用Java.net.urlConnection和Multipart/form-data编码使用其他参数上传文件?
    使用http request 上传文件上传到http server,同时也提交其他参数,java.net.net.urlconnection and Multipart/form-data Encoding是普遍的。 Here's a breakdown of the process:Mu...
    编程 发布于2025-07-06
  • 如何解决AppEngine中“无法猜测文件类型,使用application/octet-stream...”错误?
    如何解决AppEngine中“无法猜测文件类型,使用application/octet-stream...”错误?
    appEngine静态文件mime type override ,静态文件处理程序有时可以覆盖正确的mime类型,在错误消息中导致错误消息:“无法猜测mimeType for for file for file for [File]。 application/application/octet...
    编程 发布于2025-07-06
  • Java字符串非空且非null的有效检查方法
    Java字符串非空且非null的有效检查方法
    检查字符串是否不是null而不是空的 if(str!= null && str.isementy())二手: if(str!= null && str.length()== 0) option 3:trim()。isement(Isement() trim whitespace whitesp...
    编程 发布于2025-07-06
  • 如何使用node-mysql在单个查询中执行多个SQL语句?
    如何使用node-mysql在单个查询中执行多个SQL语句?
    Multi-Statement Query Support in Node-MySQLIn Node.js, the question arises when executing multiple SQL statements in a single query using the node-mys...
    编程 发布于2025-07-06

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3