您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > javascript

JavaScript内部原理:浏览器的内幕

时间:2020-03-12 10:56:34  来源:  作者:

简介

JAVAscript 是一种奇怪语言,有些人喜欢它,有些人讨厌它。它有许多独特的机制,这些机制在其他流行语言中不存在,也没有对应的机制,还有突出明显的就是代码的执行顺序

了解浏览器环境,它的组成以及它的工作原理会让我们在编写 JS 时更加自信,并为可能发生的潜在问题做好了充分的准备。

在这篇文章中,我们试着解释一下Chrome浏览器下到底发生了什么,来一起看看:

  • V8 JavaScript 引擎编译步骤,堆和内存管理,调用堆栈。
  • 浏览器运行时并发模型、事件循环、阻塞和非阻塞代码。

JavaScript引擎

最流行的JavaScript引擎是V8,它是用c++编写的,并被基于Chrome的浏览器使用,如Chrome、Opera甚至Edge。基本上,这个引擎是一个将 JS 转换成机器码并在计算机的中央处理器(CPU)上执行结果的程序。

编译

当浏览器加载 JS 文件时,V8的解析器将其转换为一个抽象语法树(AST)。该树用于生成字节码的解释器。字节码是一种可以通过编译成非优化的机器码来执行的机器码的抽象。V8在主线程中执行它,而优化编译器TurboFan在另一个线程中进行一些优化并生成优化的机器码。

这个管道称为即时(JIT)编译。

JavaScript内部原理:浏览器的内幕

 

调用堆栈

JavaScript 是一种单线程编程语言,只有一个调用堆栈。它意味着我们的代码是同步执行的。每当一个函数运行时,它将在任何其他代码运行之前完全运行。

当V8调用 JS 函数时,它必须将运行时数据存储在某个地方。调用堆栈是内存中由堆栈帧组成的位置。每个堆栈帧对应于一个尚未被调用函数。堆栈结构由以下组成:

  • 局部变量
  • argument 参数
  • 返回地址

如果我们执行一个函数,V8 会将帧推到栈顶。当我们从一个函数返回时,V8 会跳出帧。

JavaScript内部原理:浏览器的内幕

 

如上例所示,在每次函数调用时都会创建一个帧,并在每个return语句中将其删除。

其他所有内容都动态地分配到一个称为堆的大型非结构化内存块中。

堆(Heap)

有时V8在编译时不知道对象变量需要多少内存。此类数据的所有内存分配都发生在堆中。退出分配内存的函数后,堆上的对象继续存在。

V8有一个内置的垃圾收集器(GC)。垃圾收集是内存管理的一种形式。它就像一个收集器,试图释放不再使用的对象占用的内存。换句话说,当一个变量失去所有引用时,GC将该内存标记为不可访问并释放它。

我们可以通过在Chrome开发工具中创建快照来研究堆。

JavaScript内部原理:浏览器的内幕

 

实例化的每个 JS 对象都分组在其构造函数类下。括号中的分组表示不能直接调用的原生构造函数。可以看到有很多(编译代码)和(系统)实例,但也有一些传统的 JS 对象,如Math、String、Array等。

浏览器运行时

V8可以根据标准,同步地使用一个调用堆栈来执行 JS 。但,我们需要渲染UI,需要处理用户与UI的交互。此外,我们还需要在发出网络请求时处理用户交互,对此却无能为力。当所有代码都是同步的时候,我们如何实现并发呢? 这还得感谢浏览器引擎。

浏览器引擎负责用 htmlcss 渲染页面。在 Chrome 中它被称为Blink。它是WebCore的一个分支,Blink 是一个布局、渲染和文档对象模型(DOM)库。Blink 是用 c++ 中实现的,它提供了DOM元素和事件、XMLHttpRequest、fetch、setTimeout、setInterval等 Web api,这些api可以通过 JS 访问。

我们一起思考下面带有setTimeout(onTimeout, 0)的示例:

JavaScript内部原理:浏览器的内幕

 

可以看到,浏览器首先将f1()和f2()函数推入堆栈,然后执行onTimeout。那么上面的示例如何工作?

并发性

setTimeout函数执行后,浏览器引擎立即将setTimeout的回调函数放入一个事件表中。它是一个数据结构,将注册的回调映射到事件,在我们的例子中是onTimeout函数映射到timeout事件。

一旦计时器到时,在本例中,我们将延迟设为0 ms,则立即触发事件,并将onTimeout函数放入事件队列(又名回调队列,消息队列或任务队列)中。事件队列是一种数据结构,由将来要处理的回调函数(任务)组成。

最后且重要的是,事件循环(一个不断运行的循环)检查调用堆栈是否为空。如果是,则执行从事件队列中添加的第一个回调,从而移动到调用堆栈。

函数的处理将继续,直到调用堆栈再次为空。然后,事件循环将处理事件队列中的下一个回调(如果有的话)。

JavaScript内部原理:浏览器的内幕

 

 

JavaScript内部原理:浏览器的内幕

 

注意onResolve1、onResolve2和onTimeout回调的执行顺序。

阻塞和非阻塞

简单地说,所有 JS 代码都被认为是阻塞的。当 V8 忙于处理堆栈帧时,浏览器被卡住了,应用程序的 UI 被阻塞。用户将无法单击、导航或滚动。直到 V8 完成它的工作,才会处理来自网络请求的响应。

想象一下,我们如果在浏览器中运行的程序中解析图像。

 

JavaScript内部原理:浏览器的内幕

 

 

JavaScript内部原理:浏览器的内幕

 

在上面的示例中,事件循环被阻止。它无法处理事件/作业队列中的回调,因为调用堆栈包含这一帧。

Web API 为我们提供了通过异步回调来编写非阻塞代码的可能性。当调用像setTimeout或fetch这样的函数时,我们把所有的工作委托给c++原生代码,它在一个单独的线程中运行。一旦操作完成,回调就被放入事件队列。同时,V8可以继续执行 JS 代码。

使用这种并发模型,我们可以处理网络请求、用户与UI的交互等等,而不会阻塞 JS 执行线程。

总结

对于希望能够解决复杂任务的每个开发人员来说,理解 JS 环境由什么组成是至关重要的。现在我们知道了异步JavaScript是如何工作的,调用堆栈、事件循环、事件队列和作业队列在其并发模型中的角色。

你可能已经猜到的,在V8引擎和浏览器引擎后面还有很多工作要做。然而,我们大多数人只是需要对所有这些概念有一个基本的理解。如果上面的文章对你有帮助,请点击"在看"呦。


https://medium.com/better-programming/internals-under-the-hood-of-a-browser-f357378cc922作者:Vlad Ostrenko 译者:前端小智 来源:mediuum



Tags:JavaScript   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
1、通过条件判断给变量赋值布尔值的正确姿势// badif (a === 'a') { b = true} else { b = false}// goodb = a === 'a'2、在if中判断数组长度不为零...【详细内容】
2021-12-24  Tags: JavaScript  点击:(6)  评论:(0)  加入收藏
给新手朋友分享我收藏的前端必备javascript已经写好的封装好的方法函数,直接可用。方法函数总计:41个;以下给大家介绍有35个,需要整体文档的朋友私信我,1、输入一个值,将其返回数...【详细内容】
2021-12-15  Tags: JavaScript  点击:(20)  评论:(0)  加入收藏
作者:一川来源:前端万有引力 1 写在前面Javascript中的apply、call、bind方法是前端代码开发中相当重要的概念,并且与this的指向密切相关。本篇文章我们将深入探讨这个关键词的...【详细内容】
2021-12-06  Tags: JavaScript  点击:(19)  评论:(0)  加入收藏
概述DOM全称Document Object Model,即文档对象模型。是HTML和XML文档的编程接口,DOM将文档(HTML或XML)描绘成一个多节点构成的结构。使用JavaScript可以改变文档的结构、样式和...【详细内容】
2021-11-16  Tags: JavaScript  点击:(35)  评论:(0)  加入收藏
一、判断是否IE浏览器(支持判断IE11与edge)function IEVersion() {var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串var isIE = userAgent.indexOf("comp...【详细内容】
2021-11-02  Tags: JavaScript  点击:(40)  评论:(0)  加入收藏
Null、Undefined、空检查普通写法: if (username1 !== null || username1 !== undefined || username1 !== '') { let username = username1; }优化后...【详细内容】
2021-10-28  Tags: JavaScript  点击:(51)  评论:(0)  加入收藏
1、前言async函数,也就是我们常说的async/await,是在ES2017(ES8)引入的新特性,主要目的是为了简化使用基于Promise的API时所需的语法。async和await关键字让我们可以用一种更简...【详细内容】
2021-09-17  Tags: JavaScript  点击:(61)  评论:(0)  加入收藏
为什么要使用 debugger这篇文章将介绍如何使用断点来进行 JavaScript 调试。在读这篇文章之前,需要问一个问题:为什么要使用断点来进行调试?我们首先需要认可使用断点的是必要...【详细内容】
2021-08-26  Tags: JavaScript  点击:(66)  评论:(0)  加入收藏
JavaScript 可以做很多好玩的事, 从复杂的框架到处理API,有太多的东西需要学习。但是,它也能让我们只用一行就能做一些了不起的事情。1. 获得一个随机的布尔值(true/false)该函数...【详细内容】
2021-08-19  Tags: JavaScript  点击:(77)  评论:(0)  加入收藏
JavaScript 提供了大量不同的处理数组的方法,这里花几分钟时间介绍 8 个项目中可以用到的数组方法。1. Array.map()使用.map() 方法,可以创建一个基于原始数组的修订版数组。....【详细内容】
2021-08-19  Tags: JavaScript  点击:(95)  评论:(0)  加入收藏
▌简易百科推荐
1、通过条件判断给变量赋值布尔值的正确姿势// badif (a === 'a') { b = true} else { b = false}// goodb = a === 'a'2、在if中判断数组长度不为零...【详细内容】
2021-12-24  Mason程    Tags:JavaScript   点击:(6)  评论:(0)  加入收藏
给新手朋友分享我收藏的前端必备javascript已经写好的封装好的方法函数,直接可用。方法函数总计:41个;以下给大家介绍有35个,需要整体文档的朋友私信我,1、输入一个值,将其返回数...【详细内容】
2021-12-15  未来讲IT    Tags:JavaScript   点击:(20)  评论:(0)  加入收藏
1. 检测一个对象是不是纯对象,检测数据类型// 检测数据类型的方法封装(function () { var getProto = Object.getPrototypeOf; // 获取实列的原型对象。 var class2type =...【详细内容】
2021-12-08  前端明明    Tags:js   点击:(23)  评论:(0)  加入收藏
作者:一川来源:前端万有引力 1 写在前面Javascript中的apply、call、bind方法是前端代码开发中相当重要的概念,并且与this的指向密切相关。本篇文章我们将深入探讨这个关键词的...【详细内容】
2021-12-06  Nodejs开发    Tags:Javascript   点击:(19)  评论:(0)  加入收藏
概述DOM全称Document Object Model,即文档对象模型。是HTML和XML文档的编程接口,DOM将文档(HTML或XML)描绘成一个多节点构成的结构。使用JavaScript可以改变文档的结构、样式和...【详细内容】
2021-11-16  海人为记    Tags:DOM模型   点击:(35)  评论:(0)  加入收藏
入口函数 /*js加载完成事件*/ window.onload=function(){ console.log("页面和资源完全加载完毕"); } /*jQuery的ready函数*/ $(document).ready(function(){ co...【详细内容】
2021-11-12  codercyh的开发日记    Tags:jQuery   点击:(36)  评论:(0)  加入收藏
一、判断是否IE浏览器(支持判断IE11与edge)function IEVersion() {var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串var isIE = userAgent.indexOf("comp...【详细内容】
2021-11-02  V面包V    Tags:Javascript   点击:(40)  评论:(0)  加入收藏
Null、Undefined、空检查普通写法: if (username1 !== null || username1 !== undefined || username1 !== '') { let username = username1; }优化后...【详细内容】
2021-10-28  前端掘金    Tags:JavaScript   点击:(51)  评论:(0)  加入收藏
今天我们将尝试下花 1 分钟的时间简单地了解下什么是 JS 代理对象(proxies)?我们可以这样理解,JS 代理就相当于在对象的外层加了一层拦截,在拦截方法里我们可以自定义一些个性化...【详细内容】
2021-10-18  前端达人    Tags:JS   点击:(51)  评论:(0)  加入收藏
带有多个条件的 if 语句把多个值放在一个数组中,然后调用数组的 includes 方法。// bad if (x === "abc" || x === "def" || x === "ghi" || x === "jkl") { //logic } // be...【详细内容】
2021-09-27  羲和时代    Tags:JS   点击:(58)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条