JavaScript 中的变量提升、执行流程和执行上下文

一、变量提升

js 复制代码
// 变量声明与赋值 
var myname = '竹合'; // 这个代码可以看成是两部分 
// 声明 var myname; 赋值 myname = '竹合' 

// 函数声明
function foo() { 
    console.log('foo'); 
} // 直接声明函数 

var bar = function() { 
    console.log('bar'); 
} // 先声名变量var 再把函数赋值给变量bar

所谓的变量提升 ,是指JS代码执行过程中,JS引擎把变量的声明部分和函数的声明部分 提升到代码开头的行为,变量提升后,会给变量设置默认值undefined

也就是函数和变量执行之前都提升到了代码开头(执行上下文开头)

1.var声明的变量

js 复制代码
console.log(x); // undefined
var x = 5;
console.log(x); // 5

尽管 console.log(x) 出现在 var x = 5; 语句之前,但不会引发错误。在编译阶段,var x; 的声明被提升到顶部,因此第一个 console.log(x) 输出 undefined。 只有声明会被提升,而初始化不会。因此,变量 x 的值是在执行到 var x = 5; 时赋值的,第二个 console.log(x) 输出 5

2. 函数声明,整个函数都会被提升:

js 复制代码
hello(); // "Hello, world!"
function hello() {
  console.log("Hello, world!");
}

hello 函数在其声明之前被调用,这是因为整个函数声明都被提升到其所在作用域的顶部。

3. letconst 声明的变量不会被提升

js 复制代码
console.log(y); // ReferenceError: y is not defined
let y = 10;
console.log(y); // 10

原因:

  1. Temporal Dead Zone (TDZ-暂时性死区): 使用 letconst 声明的变量存在一个叫做 "Temporal Dead Zone"(时态死区)的概念。在声明变量之前的代码区域称为时态死区,在这个区域内访问变量会导致 ReferenceError
  2. Block Scoping(块级作用域): 使用 letconst 声明的变量具有块级作用域,而不是函数级作用域。这与 var 不同,它具有函数级作用域。
js 复制代码
if (true) {
  console.log(y); // ReferenceError: Cannot access 'y' before initialization
  let y = 10;
}

y 的作用域仅限于 if 语句块内,而不是整个函数或全局作用域。在声明之前访问 y 会触发时态死区。

由于这些规则,JavaScript 引擎不会将 letconst 声明的变量提升到作用域的顶部。变量只有在代码执行到达声明语句时才会被初始化,因此在声明之前的代码中访问这些变量会导致时态死区错误。这样的设计使得 JavaScript 更具可预测性和安全性。

实际上变量和函数声明在代码里的位置是不会改变的,而且在编译阶段被JS引擎放入在内存中。 在JS的内存机制种讲解变量函数如何存储的。(暂时还没写 后续补上)

二、JS代码的执行流程

  1. 解析(Parsing):

JavaScript 引擎首先会对代码进行解析,将源代码转换为抽象语法树(Abstract Syntax Tree,AST)。

  1. 编译(Compilation):

解析后,引擎会对生成的 AST 进行编译,将其转换为可执行的字节码或机器代码。有些 JavaScript 引擎(比如 V8)会使用即时编译(Just-In-Time Compilation,JIT Compilation)将字节码直接转换为机器代码。

在这里得到了AST和执行上下文后,解释器就会根据AST生成字节码,并解释执行字节码。这里可能会有疑问,不是应该转为执行效率更高的机器码吗。一开始V8是把AST直接转为机器码的,让它获得了显著的性能提升;但随着移动设备的普及,特别是在手机端,内存空间有限,我们知道机器码其实就是一堆二进制数据,很占空间,所以导致V8的这种方式,极大的消耗内存。后来为了解决这个问题,V8引入了字节码。因为字节码体积比机器码的小得多,减少了系统内存的占用。

  1. 执行(Execution):

有了字节码后,接下来解释器就开始解释执行字节码了。在解释器执行字节码的过程中,如果发现有热点代码,就是某段代码被重复执行了多次,就称为热点代码,那么编译器就会把该段热点的字节码编译为高效的机器码,然后当再次执行这段被优化的代码时,只需要执行编译后的机器码就可以了,这样就大大提升了代码的执行效率。

代码开始执行。执行过程中,JavaScript 引擎按照特定的规则和步骤逐行执行代码。

在 V8 引擎中,Y编译器指的是 TurboFan,它是 V8 引擎的即时编译器(JIT Compiler)。TurboFan 负责将 JavaScript 代码转换为高效的本地机器代码。

V8 引擎中的编译器包括两个主要组件:

  1. Crankshaft(爆震短程): Crankshaft 是 V8 引擎早期版本中的编译器,负责将 JavaScript 代码编译成优化的中间表示(Intermediate Representation,IR)。然后,它通过优化和生成本地机器代码来执行。
  2. TurboFan(涡轮扇): TurboFan 是 V8 引擎的后续版本中引入的编译器。它取代了 Crankshaft,并提供了更先进的优化技术。TurboFan 不仅负责生成本地机器代码,还能够执行更高级的优化,包括类型推断、内联缓存、多层次优化等。

TurboFan 使用了一种称为"Sea of Nodes"的表示形式,这是一种图形表示法,用于表示中间表示中的操作和数据流。这种表示形式使得 TurboFan 能够进行更灵活、更智能的优化。

三、执行上下文

执行上下文(Execution Context)是 JavaScript 中管理代码执行的环境的抽象概念。每当 JavaScript 代码在运行时,都会创建一个执行上下文,用于管理变量、函数声明、this指向等信息。

每个执行上下文都有自己的变量对象(Variable Object)、作用域链(Scope Chain)、this 指向,以及一些其他信息。执行上下文可以分为三种类型:

1. 全局执行上下文(Global Execution Context):

是默认的、最外层的执行上下文。它在整个 JavaScript 程序的生命周期中一直存在,包含了全局变量和函数。

2. 函数执行上下文(Function Execution Context):

每当调用一个函数时,都会创建一个新的函数执行上下文。每个函数都有自己的执行上下文,包含了在函数内声明的局部变量和函数参数。

3. eval 函数执行上下文:

使用 eval 函数时创建的执行上下文,但由于不推荐使用 eval,不做详细讨论,后面有说(this讲解中,还没写)。

执行上下文的生命周期包括两个阶段:

  • 创建阶段(Creation Phase):

在这个阶段,JavaScript引擎会创建变量对象、建立作用域链、确定 this 的值,并进行其他一些初始化工作。

  • 代码执行阶段(Code Execution Phase):

在这个阶段,JavaScript 引擎会按照代码的顺序执行具体的语句,给变量赋值,执行函数等。

执行上下文的栈(Execution Context Stack)是一个栈数据结构,用于管理执行上下文的顺序。当执行一个函数时,会将该函数的执行上下文推入栈中,函数执行完毕后,其执行上下文会从栈中弹出,控制权交给下一个执行上下文。

总的来说,执行上下文是 JavaScript 运行时环境的一个关键概念,它确保了代码的正确执行并提供了作用域、变量和 this 的正确解析。

  1. 在JS执行全局代码的时候,会编译全局代码并创建全局执行上下文,而且在整个页面的生命周期内,全局执行上下文只有一份

  2. 当调用一个函数的时候,函数体内的代码会被编译,并创建函数执行上下文,一般情况下,函数执行结束后,创建的函数执行上下文会被销毁。

  3. 当使用eval函数的时候,eval的代码也会被编译,并创建执行上下文。

相关推荐
Martin -Tang14 分钟前
vite和webpack的区别
前端·webpack·node.js·vite
迷途小码农零零发15 分钟前
解锁微前端的优秀库
前端
王解1 小时前
webpack loader全解析,从入门到精通(10)
前端·webpack·node.js
老码沉思录1 小时前
写给初学者的React Native 全栈开发实战班
javascript·react native·react.js
我不当帕鲁谁当帕鲁1 小时前
arcgis for js实现FeatureLayer图层弹窗展示所有field字段
前端·javascript·arcgis
那一抹阳光多灿烂1 小时前
工程化实战内功修炼测试题
前端·javascript
放逐者-保持本心,方可放逐2 小时前
微信小程序=》基础=》常见问题=》性能总结
前端·微信小程序·小程序·前端框架
毋若成4 小时前
前端三大组件之CSS,三大选择器,游戏网页仿写
前端·css
红中马喽4 小时前
JS学习日记(webAPI—DOM)
开发语言·前端·javascript·笔记·vscode·学习
Black蜡笔小新5 小时前
网页直播/点播播放器EasyPlayer.js播放器OffscreenCanvas这个特性是否需要特殊的环境和硬件支持
前端·javascript·html