Skip to content

Commit

Permalink
feat: 更新作用域&上下文,修改异步传染
Browse files Browse the repository at this point in the history
  • Loading branch information
FE-WIllLuo committed Sep 11, 2023
1 parent 1213a5b commit ca6a4ae
Show file tree
Hide file tree
Showing 5 changed files with 304 additions and 117 deletions.
9 changes: 7 additions & 2 deletions docs/.vitepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,18 @@ export default {
link: `${BLOG_PREFIX}/JavaScript中的浮点数.md`,
},
{
text: "从0搭建一个命令行CLI工具",
link: `${BLOG_PREFIX}/从0搭建一个命令行CLI工具.md`,
text: "作用域&上下文",
link: `${BLOG_PREFIX}/作用域&上下文.md`,
},
{
text: "异步传染",
link: `${BLOG_PREFIX}/异步传染.md`,
},
{
text: "从0搭建一个命令行CLI工具",
link: `${BLOG_PREFIX}/从0搭建一个命令行CLI工具.md`,
},

],
},
{
Expand Down
123 changes: 123 additions & 0 deletions docs/guide/blog/作用域&上下文.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# 作用域&上下文

你一定需要掌握的面试输出题分析方法

不讲历史原因,纯讲怎么分析

在文章开始前必须要知道作用域和上下文的区别

> 作用域(Scope)指的是在程序中定义变量的区域,它决定了变量的可见性和生命周期。作用域规定了在代码中访问变量的规则。在 JavaScript 中,主要有全局作用域和函数作用域。全局作用域中的变量可以在整个程序中访问,而函数作用域中的变量则只能在函数内部访问。作用域链用于在嵌套的作用域中查找变量。
>
> 上下文(Context)指的是代码执行时所处的环境,它包含了当前代码的执行状态和相关信息。在 JavaScript 中,主要有全局上下文和函数上下文。全局上下文是在程序启动时创建的,而函数上下文是在函数调用时创建的。每个上下文都有自己的变量对象(Variable Object)用于存储变量、函数和其他相关信息。
>
> 作用域和上下文之间的关系是这样的:作用域决定了变量的可见性和访问规则,而上下文提供了执行代码所需的环境和状态。作用域规定了在代码中如何查找变量,而上下文则提供了变量的具体值和其他执行时的信息。
>
> 简单来说,作用域关注的是变量的可见性和访问规则,而上下文关注的是代码执行时的环境和状态。
<br>

在每个函数**开始之初**,执行上下文中会创建**变量环境****词法环境**,并将当前函数执行上下文推入执行上下文栈中,当函数执行结束后会让执行上下文栈出栈

- 变量环境:
- 环境记录:包含`var`声明和`function`声明的变量
- 外部环境引用
- 词法环境:
- 环境记录:包含`let``const`声明的变量
- 外部环境引用

<br>

!!!核心要点:

**var和function声明的变量会存入最近的变量环境****而const和let声明的变量会存入最近的词法环境**

<br>

一个上下文中的变量会有三个状态

1. 声明(Declaration):当在代码中使用 `var``let``const` 或函数声明时,会进行变量的声明。在这个阶段,变量的标识符被引入到当前作用域中,但尚未分配内存空间,也没有初始化值。
2. 创建(Creation):在进入变量的作用域时,会创建该变量的内存空间。这个阶段被称为变量的创建阶段。在创建阶段,变量会被分配内存空间,并绑定到相应的作用域中。
3. 赋值(Assignment):在变量创建后,可以对变量进行赋值操作。赋值阶段是给变量分配一个具体的值或引用的过程。变量的赋值可以在声明的同时进行,也可以在稍后的代码中进行。

对于 `var` 声明的变量,会在作用域的顶部进行提升(hoisting),即在变量的作用域开始之前就进行了创建。而对于 `let``const` 声明的变量,则是在块级作用域中实际声明的位置进行创建。

<br>

作用域总共分为三种:

- 全局作用域

- 函数作用域

- 块级作用域

全局作用域和函数作用域都是老生常谈的问题了,除此之外的由`{}`包裹的就是块级作用域。

- 当程序执行到函数时会创建函数执行上下文,然后会创建变量环境和词法环境

- 但当程序在函数中执行到块级作用域时,不会创建新的上下文,只会创建一个新的词法环境

该词法环境的环境记录会存储块级作用域中由`const``var`声明的变量,`outer`指向当前函数执行上下文的词法环境,当块级作用域部分执行结束后,词法环境会被销毁

举例如下:

```ts
const name = "luowei";

function test1(){
console.log(name); // luowei
}

function test2(){
const name = "passionfruit"
test1();
}

function main() {
console.log(a); // undefined
console.log(b); // undefined
{
const test1 = 2;
var a = 1
function b() {
test2();
}
console.log(test1); // 2
}
b();
}
```

我们逐步分析函数执行过程:

调用`main`函数(左侧为`main`函数执行过程,右侧为执行上下文栈)

![1.7](/images/1.7.png)

变量查找时,从自身的环境记录开始沿着`outer`查找,这就是**作用域链**

重点讲一下为什么`test2`函数调用`test1`时,`name`值为`"luowei"`,或者说为什么`test1`的执行上下文中词法环境的`outer`指向全局执行上下文的词法环境

这里我们要说一个新名词叫**词法作用域**

词法作用域就是指作用域是由代码中的函数声明的位置来决定的,所以词法作用域是静态的作用域,通过它就能够预测代码在执行过程中如何查找标识符

<img src="/images/1.8.png" alt="1.8" style="zoom:50%;" />

所以,词法作用域是编译阶段就决定好的,和函数怎么调用的没有一点关系

<br>

那闭包又是什么?

我相信前面看完这个问题也很清晰了,闭包产生后函数的执行上下文就没有出栈,如果闭包返回了了一个函数,这个函数执行上下文中的词法环境和变量环境的`outer`指向声明时的函数上下文中词法环境和变量环境

> 所有的函数在“诞生”时都会记住创建它们的词法环境。从技术上讲,这里没有什么魔法:所有函数都有名为 `[[Environment]]` 的隐藏属性,该属性保存了对创建该函数的词法环境的引用。 ——《现代JavaScript教程》
<br>

参考文章:

[现代JavaScript教程](https://zh.javascript.info/closure)

浏览器工作原理与实践[李兵]——极客时间
Loading

0 comments on commit ca6a4ae

Please sign in to comment.