深入JavaScript的运行原理

2023-09-21 09:52:17

一、深入V8引擎原理

1.JavaScript代码的执行

JavaScript代码下载好之后,是如何一步步被执行的呢?

我们知道,浏览器内核是由两部分组成的,以webkit为例:

  • WebCore:负责HTML解析、布局、渲染等等相关的工作;
  • JavaScriptCore:解析、执行JavaScript代码;

在这里插入图片描述

另外一个强大的JavaScript引擎就是V8引擎

2.V8引擎的执行原理

我们来看一下官方对V8引擎的定义:

  • V8是用C ++编写的Google开源高性能JavaScript和WebAssembly引擎,它用于Chrome和Node.js等。
  • 它实现ECMAScript和WebAssembly,并在Windows 7或更高版本,macOS 10.12+和使用x64,IA-32,ARM或MIPS处理器的Linux系统上运行。
  • V8可以独立运行,也可以嵌入到任何C ++应用程序中。

在这里插入图片描述

3.V8引擎的架构

V8引擎本身的源码非常复杂,大概有超过100w行C++代码,通过了解它的架构,我们可以知道它是如何对JavaScript执行的:

Parse模块会将JavaScript代码转换成AST(抽象语法树),这是因为解释器并不直接认识JavaScript代码

  • 如果函数没有被调用,那么是不会被转换成AST的;
  • Parse的V8官方文档:https://v8.dev/blog/scanner

Ignition是一个解释器,会将AST转换成ByteCode(字节码)

  • 同时会收集TurboFan优化所需要的信息(比如函数参数的类型信息,有了类型才能进行真实的运算);
  • 如果函数只调用一次,Ignition会解释执行ByteCode;
  • Ignition的V8官方文档:https://v8.dev/blog/ignition-interpreter

TurboFan是一个编译器,可以将字节码编译为CPU可以直接执行的机器码

  • 如果一个函数被多次调用,那么就会被标记为热点函数,那么就会经过TurboFan转换成优化的机器码,提高代码的执行性能;
  • 但是,机器码实际上也会被还原为ByteCode,这是因为如果后续执行函数的过程中,类型发生了变化(比如sum函数原来执行的是number类型,后来执行变成了string类型),之前优化的机器码并不能正确的处理运算,就会逆向的转换成字节码;(所以建议函数的参数类型固定,有助于提高性能)
  • TurboFan的V8官方文档:https://v8.dev/blog/turbofan-jit

4.V8引擎的解析图(官方)

在这里插入图片描述

5.V8引擎的解析图

在这里插入图片描述

二、JS执行上下文

1.JavaScript代码执行原理 - 版本说明

在ECMA早期的版本中(ECMAScript3),代码的执行流程的术语和ECMAScript5以及之后的术语会有所区别:

  • 目前网上大多数流行的说法都是基于ECMAScript3版本的解析,并且在面试时问到的大多数都是ECMAScript3的版本内容。
  • 但是ECMAScript3终将过去, ECMAScript5必然会成为主流,所以最好也理解ECMAScript5甚至包括ECMAScript6以及更高版本的内容;
  • 事实上在TC39( ECMAScript5 )的最新描述中,和ECMAScript5之后的版本又出现了一定的差异

那么我们课程按照如下顺序学习:

  • 通过ECMAScript3中的概念学习JavaScript执行原理作用域作用域链闭包等概念;
  • 通过ECMAScript5中的概念学习块级作用域letconst等概念;

事实上,它们只是在对某些概念上的描述不太一样,在整体思路上都是一致的

2.JavaScript的执行过程

假如我们有下面一段代码,它在JavaScript中是如何被执行的呢?

var name = "mq";
function foo() {
    var name = 'foo';
    console.log(name);
}

var num1 = 20;
var num2 = 30;
var result = num1 + num2;

console.log(result);

foo();

三、全局代码执行过程

1.初始化全局对象

js引擎会在执行代码之前,会在堆内存中创建一个全局对象:Global Object(GO)

  • 该对象所有的作用域(scope)都可以访问;
  • 里面会包含DateArrayStringNumbersetTimeoutsetInterval等等;
  • 其中还有一个window属性指向自己;

在这里插入图片描述

2.执行上下文( Execution Contexts )

js引擎内部有一个执行上下文栈(Execution Context Stack,简称ECS),它是用于执行代码的调用栈

那么现在它要执行谁呢?执行的是全局的代码块:

  • 全局的代码块为了执行会构建一个 Global Execution Context(GEC);
  • GEC会被放入到ECS中执行;

GEC 被放入到ECS中里面包含两部分内容:

  • 第一部分:在代码执行前,在parser转成AST的过程中,会将全局定义的变量、函数等加入到GlobalObject中,但是并不会赋值;
    • 这个过程也称之为变量的作用域提升(hoisting)
  • 第二部分:在代码执行中,对变量赋值,或者执行其他的函数;

在这里插入图片描述

3.认识VO对象(Variable Object)

每一个执行上下文会关联一个VO(Variable Object,变量对象),变量和函数声明会被添加到这个VO对象中。

在这里插入图片描述

当全局代码被执行的时候,VO就是GO对象了

在这里插入图片描述

4.全局代码执行过程(执行前)

在这里插入图片描述

5.全局代码执行过程(执行后)

在这里插入图片描述

四、函数代码执行过程

1.函数如何被执行呢?

在执行的过程中执行到一个函数时,就会根据函数体创建一个函数执行上下文(Functional Execution Context,简称FEC), 并且压入到EC Stack中。

因为每个执行上下文都会关联一个VO,那么函数执行上下文关联的VO是什么呢?

  • 当进入一个函数执行上下文时,会创建一个AO对象(Activation Object);
  • 这个AO对象会使用arguments作为初始化,并且初始值是传入的参数;
  • 这个AO对象会作为执行上下文的VO来存放变量的初始化;

在这里插入图片描述

2.函数的执行过程(执行前)

在这里插入图片描述

3.函数的执行过程(执行后)

在这里插入图片描述

五、作用域和作用域链

当进入到一个执行上下文时,执行上下文也会关联一个作用域链(Scope Chain)

  • 作用域链是一个对象列表,用于变量标识符的求值;
  • 当进入一个执行上下文时,这个作用域链被创建,并且根据代码类型,添加一系列的对象;

在这里插入图片描述

1.作用域提升面试题

var n = 100
function foo() {
    n = 200
}
foo()

console.log(n)//200
function foo() {
    console.log(n)
    var n = 200
    console.log(n)
}
var n = 100
foo()
// undefined
// 200
var a = 100

function foo() {
    console.log(a)
    return
    var a = 100
}
foo()//undefined
function foo() {
    var a = b = 100
}
foo()
console.log(a)//报错,变量名未定义
console.log(b)//100
var n = 100
function foo1() {
    console.log(n)
}

function foo2() {
    var n = 200
    console.log(n)
    foo1()
}

foo2()
// 200
// 100
console.log(n)
// 100
更多推荐

【栈和队列面试题】用栈实现队列(动图解析更清晰)

leetcode232.用栈实现队列前言:用两个栈实现一个队列,模拟实现队列的功能。💥🎈个人主页:​​​​​​Dream_Chaser~🎈💥✨✨刷题专栏:http://t.csdn.cn/UlvTc💨💨本篇内容:力扣上栈与队列面试题目目录leetcode232.用栈实现队列📌结构体类型的声明(MyQueu

开发需知的文件加密与解密

背景最近团队遇到一个小需求,存在两个系统A、B,系统A支持用户在线制作皮肤包,制作后的皮肤包用户可以下载后,导入到另外的系统B上。皮肤包本身的其实就是一个zip压缩包,系统B接收到压缩包后,解压并做一些常规的校验,比如版本、内容合法性校验等,整体功能也比较简单。但没想到啊,一帮测试人员对我们开发人员一顿输出,首先绕过系

Spring Cloud Alibaba Nacos 2.2.3 (1) - 下载与数据库配置

这里写自定义目录标题下载nacos修改配置文件application.properties执行数据库脚本下载nacosnacos在GitHub上有下载地址:https://github.com/alibaba/nacos/releases,可以选择任意版本下载。我下载的是2.2.3版本修改配置文件application

LeetCode 面试题 04.08. 首个共同祖先

文章目录一、题目二、C#题解一、题目设计并实现一个算法,找出二叉树中某两个节点的第一个共同祖先。不得将其他的节点存储在另外的数据结构中。注意:这不一定是二叉搜索树。例如,给定如下二叉树:root=[3,5,1,6,2,0,8,null,null,7,4]3/\51/\/\6208/\74点击此处跳转题目。示例1:输入:

Kafka消费者组重平衡(二)

文章目录概要重平衡通知机制消费组组状态消费端重平衡流程Broker端重平衡流程概要上一篇Kafka消费者组重平衡主要介绍了重平衡相关的概念,本篇主要梳理重平衡发生的流程。为了更好地观察,数据准备如下:kafka版本:kafka_2.13-3.2.1控制台创建topic(2个分区1个副本):bin/kafka-topic

详细指南:基于差分进化的马尔可夫链蒙特卡罗加速技术在MATLAB中的应用

第一部分:概念简介与基础知识1.什么是马尔可夫链蒙特卡罗(MarkovChainMonteCarlo,MCMC)?马尔可夫链蒙特卡罗是一种通过马尔可夫链来估计复杂分布的统计方法。通过构建一个特定的马尔可夫链,使其平稳分布等于目标分布,我们可以从该马尔可夫链中抽取样本来估计目标分布的统计性质。2.差分进化(Differe

实战SRC漏洞挖掘全过程,流程详细【网络安全】

前言记录一次完整的某SRC漏洞挖掘实战,为期一个多星期。文章有点长,请耐心看完,记录了完整的SRC漏洞挖掘实战渗透过程因为选择的幸运儿没有对测试范围进行规划,所以此次范围就是没有范围。先上主域名看一眼,看看能收集到什么有效信息:发现存在搜索框:测试点+1对页面点点点没发现什么有用的页面。抓包看看,发现网站搭建了CDN,

Elasticsearch:什么是向量和向量存储数据库,我们为什么关心?

Elasticsearch从7.3版本开始支持向量搜索。从8.0开始支持带有HNSW的ANN向量搜索。目前Elasticsearch已经是全球下载量最多的向量数据库。它允许使用密集向量和向量比较来搜索文档。矢量搜索在人工智能和机器学习领域有许多重要的应用。有效存储和检索向量的数据库对于构建生产就绪的AI/ML服务至关重

您距离一个成熟安全的 DevOps 平台,只差一个迁移

历经14年的发展后,DevOps已经不再是一个鲜为人知的术语,国内外众多企业在成熟方法论和复杂工具链的加持下,通过DevOps的落地实践实现了软件交付效率的提升。随着DevOps的深入发展,DevOps的市场规模也在进一步快速发展。根据ResearchandMarkets的调研数据,2020年全球DevOps市场规模大

springboot和vue:阿里云云服务器ECS的购买与基础参数配置+XShell远程连接服务器

云服务器ECS的特点弹性水平拓展的应用场景:譬如微博热搜爆了,则需要水平拓展。其他功能ECS一般提供自动宕机迁移、数据备份和回滚、系统性能报警等功能,稳定性更高阿里云服务器的购买(针对学生党学习自用)其他:推荐支付宝登录创建ECS登陆进去后可以从两个地方创建ECS。地方一:直接点击创建我的ECS(如果你没有买过的话)地

十二、LCD1602

十二、LCD1602介绍功能函数介绍引脚和应用电路时许结构功能函数#include<REGX52.H>//引脚定义sbitLCD_RS=P2^6;sbitLCD_RW=P2^5;sbitLCD_E=P2^7;#defineLCD_DataPortP0/***@briefLCD1602延时函数,12MHz调用可延时1ms

热文推荐