人人都看得懂的JS运行机制(中)

简介: 本文是以作者自己理解的思路拆分的JS运行机制,见解有限难免疏漏,欢迎留言勘误、交流。

JS引擎


根据上文的描述,JS源代码是需要经过JS引擎解析、转化之后才执行的。


通常认为,JS引擎主要有两部分组成:


  • 内存堆 引用类型实际值、内存分配的地方


  • 调用栈 基本类型存储、引用类型地址名存储、代码逻辑执行的地方


源代码进入JS引擎之后,顺序读取代码,按照变量声明、函数执行等不同规则,分配到堆、或者栈内。



内存堆


存储项目引用类型数据的地方, 系统分配的内存,


JS中的引用类型数据,实际值是零散地存在这里面的


事实上,引用类型的存储是分为2部分存储的:


  • 真实值存储在内存中, 是系统根据自身情况,内存区哪里有合适的位置,就分配在哪里,没有严格的顺序的,因此说是零散的


  • 真实值所在的物理内存地址,这个值是以基本值的形式存储在栈内的


平时代码中的引用类型赋值,就是仅仅把栈内存储的内存地址赋给新变量,就相当于是告诉新变量该值在内存中的位置,需要的时候去取就行,并不是把真正的值传递过去,内存中该值是只有一份的。这也是引起引用问题的原因


执行栈


执行栈,是代码中实际逻辑语句执行的地方,同时项目运行过程中产生的基本类型的值也是存在此处。


引擎会把代码分成一个个可执行单元,然后依次进入执行栈,被执行

那么可执行单元是什么?


可执行单元,标准的说法是执行上下文


JS中,执行上下文可以是以下几种:


  • Global code   ----->  全局执行上下文


  • Function code   ----->  函数执行上下文


  • Eval code   ----->  eval函数执行上下文


这些东西有什么共同点?


全局代码可以看作是一个IIFE(立即执行函数),


函数就是通俗意义上的函数


eval 是可以把传入字符串执行的函数


函数啊, 全部都是函数啊


因此我们可以粗略的理解为: 执行栈里面的东西,都是一个个函数调用


  • 首先是入口文件的全部JS代码作为一个IIFE,最先入栈被调用


  • 然后在实际执行过程中,调用了其他函数,就会顺次被压入栈内执行


因此, JS引擎的示意图可以更新为如下:



单线程


JS是单线程的。  地球人都知道。


什么意思?


JS引擎中,代码执行是在调用栈的里发生的。


栈是一种LIFO(last in first out 后进先出)的数据结构。


只有栈顶的函数会被处理, 处理完成之后弹出栈, 后面的进入栈顶,再被执行...

举个🌰:


如下的JS文件


function first() {
  second();  
}
function second() {  
  console.log('log fn');  
}
first();
... // 后续操作



JS引擎处理这段代码的步骤如下 (只关注函数调用)


  1. 整段代码作为一个IIFE,入栈, main()函数调用进入栈顶,开始执行


  1. 遇到first函数调用, first函数进入栈顶,开始进入first函数体执行 (此时main函数还未执行完毕)


  1. 进入first函数体之后,遇到调用second函数, 把second函数压入栈顶,进入second函数体执行


  1. 进入second函数体之后,遇到console.log函数调用, 把console.log压入栈顶执行


  1. console.log函数打印完毕之后,执行结束, 弹出栈顶


  1. 此时栈顶是second函数,继续执行second函数体中,console.log之后的代码,发现没有可执行代码了,OK,那就宣告second执行完毕,弹出


  1. 此时first函数进入栈顶,那就执行first函数体中,second函数调用之后的代码,发现空空如也,那么first函数执行完毕,弹出


  1. 栈顶main函数执行后续代码


因此JS单线程,指的是在JS引擎中,解析执行JS代码的调用栈是唯一的,所有的JS代码都在这一个调用栈里按照调用顺序执行,不能同时执行多个函数


相关文章
|
1月前
|
设计模式 JavaScript 前端开发
深入理解 JavaScript 中的绑定机制(下)
深入理解 JavaScript 中的绑定机制(下)
|
1月前
|
JavaScript 前端开发
深入理解 JavaScript 中的绑定机制(上)
深入理解 JavaScript 中的绑定机制(上)
|
3月前
|
JavaScript 前端开发 数据库连接
js的异常程序处理机制
js的异常程序处理机制
18 0
|
3月前
|
存储 前端开发 JavaScript
深入理解前端JavaScript执行机制
深入理解前端JavaScript执行机制
43 0
|
2月前
|
消息中间件 Web App开发 JavaScript
Node.js【简介、安装、运行 Node.js 脚本、事件循环、ES6 作业队列、Buffer(缓冲区)、Stream(流)】(一)-全面详解(学习总结---从入门到深化)
Node.js【简介、安装、运行 Node.js 脚本、事件循环、ES6 作业队列、Buffer(缓冲区)、Stream(流)】(一)-全面详解(学习总结---从入门到深化)
77 0
|
1月前
|
开发框架 JavaScript 前端开发
描述JavaScript事件循环机制,并举例说明在游戏循环更新中的应用。
JavaScript的事件循环机制是单线程处理异步操作的关键,由调用栈、事件队列和Web APIs构成。调用栈执行函数,遇到异步操作时交给Web APIs,完成后回调函数进入事件队列。当调用栈空时,事件循环取队列中的任务执行。在游戏开发中,事件循环驱动游戏循环更新,包括输入处理、逻辑更新和渲染。示例代码展示了如何模拟游戏循环,实际开发中常用框架提供更高级别的抽象。
14 1
|
1月前
|
JavaScript 前端开发 算法
深入探讨前端框架Vue.js中的虚拟DOM机制
本文将深入探讨前端框架Vue.js中的虚拟DOM机制,分析其原理、优势以及在实际开发中的应用场景,帮助读者更好地理解Vue.js框架的核心特性。
|
1月前
|
消息中间件 前端开发 JavaScript
深入理解JavaScript中的事件循环机制
JavaScript作为一种前端开发必备的编程语言,在处理异步操作时常常涉及到事件循环机制。本文将深入探讨JavaScript中事件循环的工作原理,帮助读者更好地理解和运用这一关键概念。
|
1月前
|
JavaScript 前端开发 开发者
如果你想在钉钉环境中运行JavaScript脚本
【2月更文挑战第17天】如果你想在钉钉环境中运行JavaScript脚本
34 6
|
1月前
|
自然语言处理 JavaScript 前端开发
深入探索 JS 的提升机制、函数与块作用域以及函数表达式和声明(下)
深入探索 JS 的提升机制、函数与块作用域以及函数表达式和声明(下)