2024-09-04 深入JavaScript高级语法十五——浏览器原理-V8引擎-js执行原理

news2024/11/19 15:15:19

目录

  • 1、浏览器的工作原理
    • 1.1、认识浏览器内核
    • 1.2、浏览器渲染过程
  • 2、JS引擎
    • 2.1、认识 JavaScript 引擎
    • 2.2、浏览器内核和JS引擎的关系
    • 2.3、V8引擎的原理
    • 2.4、V8引擎的架构
    • 2.5、V8执行的细节
  • 3、全局代码的执行过程
    • 3.1、初始化全局对象
    • 3.2、执行上下文栈(调用栈)
    • 3.3、全局代码里包含函数时的执行过程
    • 3.4、函数调用函数的执行过程-引出作用域提升的概念
    • 3.5、概念:变量环境和记录
    • 3.6、作用域提升面试题
    • 3.7、vscode中的画图插件

1、浏览器的工作原理

在这里插入图片描述

1.1、认识浏览器内核

  • 我们经常会说:不同的浏览器由不同的内核组成
  • Gecko :早期被 Netscape 和 Mozilla Firefox 浏览器浏览器使用;
  • Trident :微软开发,被IE4~IE11浏览器使用,但是 Edge 浏览器已经转向 Blink ;
  • Webkit :苹果基于 KHTML 开发、开源的,用于 Safari , Google Chrome 之前也在使用;
  • Blink :是 Webkit 的一个分支, Google 开发,目前应用于 Google Chrome 、 Edge 、 Opera 等;
  • 等等…
  • 事实上,我们经常说的浏览器内核指的是浏览器的排版引擎:
  • 排版引擎( layout engine ),也称为浏览器引擎( browser engine )、页面渲染引擎( rendering engine )或样版引擎。

1.2、浏览器渲染过程

  • 在下图的执行过程中, HTML 解析的时候遇到了 JavaScript 标签,应该怎么办呢?
  • 会停止解析 HTML ,而去加载和执行 JavaScript 代码;

在这里插入图片描述

  • 那么, JavaScript 代码由谁来执行呢?
  • JavaScript 引擎

2、JS引擎

2.1、认识 JavaScript 引擎

  • 为什么需要 JavaScript 引擎呢?
  • 我们前面说过,高级的编程语言都是需要转成最终的机器指令来执行的;
  • 事实上我们编写的 JavaScript 无论你交给浏览器或者 Node 执行,最后都是需要被 CPU 执行的;
  • 但是 CPU 只认识自己的指令集,实际上是机器语言,才能被 CPU 所执行;
  • 所以我们需要 JavaScript 引擎帮助我们将 JavaScript 代码翻译成 CPU 指令来执行;
  • 比较常见的 JavaScript 引擎有哪些呢?
  • SpiderMonkey :第一款 JavaScript 引擎,由 Brendan Eich 开发(也就是 JavaScript 作者);
  • Chakra :微软开发,用于 IE 浏览器;
  • JavaScriptCore : WebKit 中的 JavaScript 引擎, Apple 公司开发;
  • V8:Google开发的强大 JavaScript 引擎,也帮助 Chrome 从众多浏览器中脱颖而出;
  • 等等…

2.2、浏览器内核和JS引擎的关系

  • 这里我们先以 WebKit 为例, WebKit 事实上由两部分组成的:
  • WebCore :负责 HTML 解析、布局、渲染等等相关的工作;
  • JavaScriptCore :解析、执行 JavaScript 代码;

在这里插入图片描述

  • 看到这里,学过小程序的同学有没有感觉非常的熟悉呢?
  • 在小程序中编写的 JavaScript 代码就是被 JSCore 执行的;

在这里插入图片描述

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

2.3、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 ++应用程序中。

2.4、V8引擎的架构

AST解析查看
在这里插入图片描述

  • V8引擎本身的源码非常复杂,大概有超过100w行 C ++代码,通过了解它的架构,我们可以知道它是如何对 JavaScript 执行的:
  • Parse模块会将 JavaScript 代码转换成 AST (抽象语法树),这是因为解释器并不直接认识 JavaScript 代码;
  • 如果函数没有被调用,那么是不会被转换成 AST 的;
  • Parse的V8官方文档:Parse的V8官方文档
  • Ignition 是一个解释器,会将 AST 转换成 ByteCode (字节码)
  • 同时会收集 TurboFan 优化所需要的信息(比如函数参数的类型信息,有了类型才能进行真实的运算);
  • 如果函数只调用一次, Ignition 会解析执行 ByteCode ;
  • Ignition的V8官方文档:Ignition的V8官方文档
  • TurboFan 是一个编译器,可以将字节码编译为 CPU 可以直接执行的机器码;
  • 如果一个函数被多次调用,那么就会被标记为热点函数,那么就会经过 TurboFan 转换成优化的机器码,提高代码的执行性能;
  • 但是,机器码实际上也会被还原为 ByteCode ,这是因为如果后续执行函数的过程中,类型发生了变化(比如 sum 函数原来执行的是 number 类型,后来执行变成了 string 类型),之前优化的机器码并不能正确的处理运算,就会逆向的转换成字节码;
  • TurboFan的V8官方文档: TurboFan的V8官方文档

2.5、V8执行的细节

  • 那么我们的 JavaScript 源码是如何被解析( Parse 过程)的呢?
  • Blink 将源码交给V8引擎, Stream 获取到源码并且进行编码转换;
  • Scanner 会进行词法分析( lexical analysis ),词法分析会将代码转换成 tokens ;
  • 接下来 tokens 会被转换成 AST 树,经过 Parser 和 PreParser :
  • Parser 就是直接将 tokens 转成 AST 树架构;
  • PreParser 称之为预解析,为什么需要预解析呢?
    √ 这是因为并不是所有的 JavaScript 代码,在一开始时就会被执行。那么对所有的 JavaScript 代码进行解析,必然会影响网页的运行效率;
    √ 所以V8引擎就实现了 Lazy Parsing (延迟解析)的方案,它的作用是将不必要的函数进行预解析,也就是只解析暂时需要的内容,而对函数的全量解析是在函数被调用时才会进行;
    √ 比如我们在一个函数 outer 内部定义了另外一个函数 inner ,那么 inner 函数就会进行预解析;
  • 生成 AST 树后,会被 lgnition 转成字节码( bytecode ),之后的过程就是代码的执行过程(后续会详细分析)。

在这里插入图片描述

3、全局代码的执行过程

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

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

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

console.log(result);

foo()

3.1、初始化全局对象

  • js 引擎会在执行代码之前,会在堆内存中创建一个全局对象: Global Object ( GO )口该对象所有的作用域( scope )都可以访问;
  • 该对象所有的作用域( scope )都可以访问;
  • 里面会包含 Date 、 Array 、 String 、 Number 、 setTimeout 、 setInterval 等等;
  • 其中还有一个 window 属性指向自己;

在这里插入图片描述

3.2、执行上下文栈(调用栈)

  • js 引擎内部有一个执行上下文栈( Execution Context Stack ,简称 ECS ),它是用于执行代码的调用栈。
  • 那么现在它要执行谁呢?执行的是全局的代码块:
  • 全局的代码块为了执行会构建一个 Global Execution Context ( GEC );
  • GEC 会被放入到 ECS 中执行;
  • GEC 被放入到 ECS 中里面包含两部分内容:
  • 第一部分:在代码执行前,在 parser 转成 AST 的过程中,会将全局定义的变量、函数等加入到 GlobalObject 中,但是并不会赋值;
    这个过程也称之为变量的作用域提升( hoisting )
    在这里插入图片描述
  • 第二部分:在代码执行中,对变量赋值,或者执行其他的函数;
    在这里插入图片描述

3.3、全局代码里包含函数时的执行过程

  • 在执行的过程中执行到一个函数时,就会根据函数体创建一个函数执行上下文( Functional Execution Context ,简称 FEC ),并且压入到 EC Stack 中。
  • FEC 中包含三部分内容:
  • 第一部分:在解析函数成为 AST 树结构时,会创建一个 Activation Object ( AO ):
    √ AO 中包含形参、 arguments 、函数定义和指向函数对象、定义的变量;
  • 第二部分:作用域链:由 VO (在函数中就是 AO 对象)和父级 VO 组成,查找时会一层层查找;
  • 第三部分: this 绑定的值;
    在这里插入图片描述
  • 只有一层函数
var name = 'why'

foo(123)
function foo(num) {
    console.log(m);
    var m = 10
    var n = 20
    console.log('foo');
}

在这里插入图片描述

  • 函数嵌套时
var name = 'why'

foo(123)
function foo(num) {
    console.log(m);
    var m = 10
    var n = 20
    function bar() {
        console.log(name);
    }

    bar()
}

在这里插入图片描述

3.4、函数调用函数的执行过程-引出作用域提升的概念

foo的父级作用域是GO。函数的父级作用域跟它的调用位置无关,只跟它的定义位置有关。

var message = 'hello global'

function foo() {
    console.log(message); // 'hello global'
}

function bar() {
    var message = 'hello bar'
    foo()
}

bar()

在这里插入图片描述

3.5、概念:变量环境和记录

  • 其实我们上面的讲解都是基于早期 ECMA 的版本规范:

Every execution context has associated with it a variable object . Variables and functions declared in the source text are added as properties of the variable object . For function code , parameters are added as properties of the variable object .
每一个执行上下文会被关联到一个变量环境( variable object , VO ),在源代码中的变量和函数声明会被作为属性添加到 VO 中。
对于函数来说,参数也会被添加到 VO 中。

  • 在最新的 ECMA 的版本规范中,对于一些词汇进行了修改:

Every execution context has an associated VariableEnvironment . Variables and functions declared in ECMAScript code evaluated in an execution context are added as bindings in that VariableEnvironment ’ s Environment Record . For function code , parameters are also added as bindings to that Environment Record .
每一个执行上下文会关联到一个变量环境( VariableEnvironment )中,在执行代码中变量和函数的声明会作为环境记录( Environment Record )添加到变量环境中。
对于函数来说,参数也会被作为环境记录添加到变量环境中。

  • 通过上面的变化我们可以知道,在最新的 ECMA 标准中,我们前面的变量对象 VO 已经有另外一个称呼了变量环境 VE 。

3.6、作用域提升面试题

  • 面试题一
var n = 100
function foo() {
    n = 200
}

foo()

console.log(n);
  • 面试题二
function foo() {
    console.log(n);
    var n = 200
    console.log(n);
}

var n = 100
foo()
  • 面试题三
var n = 100
function foo1() {
    console.log(n);
}
function foo2() {
    var n = 200
    console.log(n);
    foo1()
}

foo2()
console.log(n);
  • 面试题四
var a = 100
function foo() {
    console.log(a);
    return
    var a = 100
}
foo()
  • 面试题五
    代码会报错,m is not defined
function foo() {
    var m = 100
}

foo()
console.log(m);
  • 面试题六
    注意:这种写法在严格意义上算是一种语法错误,只是在JS引擎中对这种语法做了特殊处理,可能会出现在面试题中,但实际开发千万不要写这种代码。
function foo() {
    m = 100
}

foo()
console.log(m);
  • 面试题七
function foo() {
    var a = b = 10
    // => 转成下面的两行代码
    // var a = 10
    // b = 10
}

foo()

console.log(a);
console.log(b);

3.7、vscode中的画图插件

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2183774.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

World of Warcraft [CLASSIC][80][Grandel] Call to Arms: Victory in Wintergrasp

Wintergrasp 冬拥湖 120 VS 120 Victory in Wintergrasp - Quest - 魔兽世界怀旧服WLK3.35数据库_巫妖王之怒80级魔兽数据库_wlk数据库

逆向-下字符串查找的条件断点

为了跟踪console程序在访问某个文件时失败的问题,在内核中下了断点,但是内核中文件部分调用太频繁了,无法等到自己的文件。所以最好还是根据条件来下断点。 程序如下 想要在FileName是指定文件时停下来,例如FileName是c:\temp\f…

「轻盈」之旅:OOM故障重现与解决

前期准备 本项目均采用 VisualVM 2.1.10 进行dump文件的分析。JDK1.8及之前所在目录的bin目录下有自带的VisualVM,JDK1.8以后需要自行手动安装下载。 下载地址:https://visualvm.github.io/download.html IDEA插件配置:在Plugins里搜索visual…

2-109 基于matlab-GUI的BP神经网络

基于matlab-GUI的BP神经网络,10种不同分布的数据样本,9种不同的激活函数,可更改升级网络结构参数,对比各种方法参数下的训练测试效果,实时显示预测过程。程序已调通,可直接运行。 下载源程序请点链接&…

【简介Sentinel-1】

Sentinel-1是欧洲航天局哥白尼计划(GMES)中的地球观测卫星,由Sentinel-1A和Sentinel-1B两颗卫星组成。以下是对Sentinel-1的详细介绍: 一、基本信息 卫星名称:Sentinel-1 所属计划:欧洲航天局哥白尼计划…

【CSS】兼容处理

兼容前缀兼容查询 由于不同浏览器对CSS标准的支持程度不同,可能会导致在不同浏览器中出现样式差异。为了解决这个问题,需要采取一些措施来提高CSS的兼容性 兼容前缀 兼容前缀针对的浏览器-webkit-WebKit 内核浏览器,如:Safari 、…

.NET Core 集成 MiniProfiler性能分析工具

前言: 在日常开发中,应用程序的性能是我们需要关注的一个重点问题。当然我们有很多工具来分析程序性能:如:Zipkin等;但这些过于复杂,需要单独搭建。 MiniProfiler就是一款简单,但功能强大的应用…

进击J9:Inception v3算法实战与解析

🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 一、实验目的: 了解并学习InceptionV3相对于InceptionV1改进了哪些地方(重点)使用Inception v3完成天气识别案例 二、实验环…

Android 12.0 关于定制自适应AdaptiveIconDrawable类型的动态时钟图标的功能实现系列一

1.前言 在12.0的系统rom定制化开发中,在关于定制动态时钟图标中,原系统是不支持动态时钟图标的功能,所以就需要从新 定制动态时钟图标关于自适应AdaptiveIconDrawable类型的样式,就是可以支持当改变系统图标样式变化时,动态时钟 图标的背景图形也跟着改变,所以接下来就来…

OpenFeign微服务部署

一.开启nacos 和redis 1.查看nacos和redis是否启动 docker ps2.查看是否安装nacos和redis docker ps -a3.启动nacos和redis docker start nacos docker start redis-6379 docker ps 二.使用SpringSession共享例子 这里的两个例子在我的一个博客有创建过程&#xff0c…

通信工程学习:什么是LTE长期演进

LTE:长期演进 LTE(Long Term Evolution,长期演进)是由3GPP(The 3rd Generation Partnership Project,第三代合作伙伴计划)组织制定的UMTS(Universal Mobile Telecommunications System,通用移动通信系统)技术标准的长期演进。以下是对LTE的详细解释: 一、定…

音乐制作软件FL Studio 24.1.1.4285 中文完整版新功能介绍及如何安装激活FL Studio 24

FL Studio 24.1.1.4285 中文完整版又被国内网友称之为水果音乐制作软件24,是Image-Line公司成立26周年而发布的一个版本,是目前互联网上最优秀的完整的软件音乐制作环境或数字音频工作站,包含了编排,录制,编辑&#xf…

笔墨歌盛世 丹青绘匠心,艺术赋能“百千万工程”

9月30日上午,乡村有“艺”思——2024 年三乡镇乡村文化艺术周启动仪式暨“崛起的力量”余镇河深中通道主题美术作品展开幕仪式在中山市三乡镇古鹤村成荣美术馆举行。 中山市文联党组成员、专职副主席卢曙光,三乡镇党委委员艾立强,中山市文化馆…

leetcode每日一题day21(24.10.1)——最低票价

看到题目,最低消费又有各种的方案,与结合往期每日一题很就没出动态规划,就感觉这题很像动态规划。 思路:对于第X天,买票有三种方案,即从,X-1天买一天的票,X-7买7天的票,X-30买三十天…

iSTFT 完美重构的条件详解

目录 引言1. 短时傅里叶变换(STFT)与逆变换(iSTFT)概述2. 完美重构的条件3. 数学推导4. 实现要点5. 示例代码6. 总结 引言 在数字信号处理领域,短时傅里叶变换(Short-Time Fourier Transform,简…

Java Web开发详解:从入门到实践

目录 引言 Java Web开发的优势 Java Web开发核心概念 Servlet和JSP Servlet JSP(JavaServer Pages) MVC架构 JDBC和数据库访问 JDBC概述 数据库连接示例 常用的Java Web框架 Spring MVC Hibernate MyBatis 对比常用框架 Java Web开发流程…

YOLOv5改进系列(1)——添加CBAM注意力机制

一、如何理解注意力机制 假设你正在阅读一本书,同时有人在你旁边说话。当你听到某些关键字时,比如“你的名字”或者“你感兴趣的话题”,你会自动把注意力从书上转移到他们的谈话上,尽管你并没有完全忽略书本的内容。这就是注意力机…

docker零基础入门教程

注意 本系列文章已升级、转移至我的自建站点中,本章原文为:Docker入门 目录 注意1.前言2.docker安装3.docker基本使用4.打包docker镜像5.docker进阶 1.前言 如果你长期写C/C代码,那你应该很容易发现C/C开源项目存在的一个严重问题&#xff…

Xshell-8下载安装教程

下载地址 https://www.xshell.com/zh/free-for-home-school/ 新建Xshell文件夹 点击安装程序 选择新建Xshell文件夹 默认即可 点击安装 注册 提交后点击邮箱收到的链接 点击确认 安装完成

【C/C++】错题记录(三)

题目一 题目二 题目三 题目四 题目五 题目六 题目七??? 题目八 这道题主要考查对数据类型和位运算的理解与运用。 分析选项 A: *((unsigned char *)(&number) 1)0xcd; 这里将 number 的地址强制转换为 unsigned char* 类型&a…