【译】JavaScript如何工作的:一览引擎、运行时和调用栈
随着JavaScript(下文简称js)越来越流行,它在各个层面上都留下了身影:前端、后端、hybrid app、嵌入式设备等。
这篇文章是这个系列中第一个深入挖掘js是如何工作的:我们认为理解了js的底层建筑和运行方式可以使我们写出更好的代码和应用。
总览
应该很多人都听过V8引擎这个概念,也知道js是一个单线程的语言,还有它使用了回调队列。
这篇文章里我们会逐一解析每一个概念并解释js是怎么运行的。
如果你是js的初学者,那么这篇文章会让你了解为什么js比起其他语言会那么的“奇怪”。
如果你是js的高手,它会带给你一些关于你每天使用的js运行时是怎样工作的新颖知识。
JavaScript引擎
一个流行的js引擎是谷歌的V8引擎。它被使用在Chrome和Node.js中。这里简单地描述了他是什么样的:
引擎包含量两大部分:
- 内存堆(Memory Heap)——内存分配的地方
- 调用栈——这是你的代码执行时栈帧的位置
运行时
在浏览器中有很多被几乎每一位开发者使用的API(比如setTimeout
)。这些API,却并不是引擎提供的。
所以,他们来自哪里?
事实上要复杂一点点。
除了引擎以外还有一些东西。我们把这些浏览器提供的东西叫做Web API,比如DOM,AJAX,setTimeout等。
图下方是大名鼎鼎的事件循环(event loop)和回调队列( callback queue)。
调用堆栈(Call Stack)
Js是一个单线程的语言。所以它也只有一个调用栈。这意味着它同时只能做一件事件。
调用栈是一个数据结构,基本上它记录了我们的程序运行到哪了。如果我们运行进一个函数,那么我们把它放在堆栈的顶部。如果我们从一个函数返回,那么我们弹出(pop off)堆栈顶部的函数。这是堆栈所做的工作。
我们来看一个例子:
function multiply(x, y) { return x * y; } function printSquare(x) { var s = multiply(x, x); console.log(s); } printSquare(5); 复制代码
当我们的引擎刚开始执行上述代码的时候,调用栈会是空。之后的步骤会如下图所示:
调用栈中的每一条都称作栈帧(Stack Frame)
这也解释了当发生异常的时候堆栈轨迹( stack traces)是怎么被建立起来的——其实就是异常发生时调用栈的状态。看下面的列子:
function foo() { throw new Error('SessionStack will help you resolve crashes :)'); } function bar() { foo(); } function start() { bar(); } start(); 复制代码
如果在Chrome中执行(假设运行的文件叫foo.js),会产生下面的堆栈轨迹:
"Blowing the stack"——这个异常发生在你达到了调用栈最大值的时候。这个很容易出现,特别是在你不小心错误地使用了递归的时候:
function foo() { foo(); } foo(); 复制代码
当引擎开始执行代码,我们会无止尽地执行这个函数。所以这个函数被不断地堆在调用栈上面,就像这样:
这时候浏览器会爆出:
代码运行在单线程的环境是一个很轻松的事,你不必担心一些多线程带来的复杂场景——比如,死锁。
但是单线程也有一些限制。js只有一个调用栈,如何其中一些东西运行很慢怎么办?
并发 & 时间循环
当您在调用栈中行调用需要花费大量时间才能的函数时,会发生什么情况?比如你想在浏览器中进行图像处理。
你可能会问——这为什么会有问题?问题在于调用栈中有函数在执行,浏览器不能做其他的事情。这意味着浏览器不能渲染,不能跑其他代码。这会成为流畅UI界面的阻碍。
而且,一旦你的浏览器要处理太多的任务了,它会失去响应。一些浏览器会采取行动,询问你是否终止这个网页。
所以我们不卡死浏览器且拥有流畅UI的情况下执行大量代码呢?解决方案是异步调用。
这会在本系列的下一篇文章中提到:“Inside the V8 engine + 5 tips on how to write optimized code”。(译注:后续翻译尽请关注)
作者:Skandar-Ln
链接:https://juejin.im/post/5b346d416fb9a00e6a621cc4
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Docker 常用命令总结
列出镜像列表 docker images 获取新的镜像 docker pull centos:7.0 查找镜像 docker search httpd 更新镜像 docker commit -m="提交的信息" -a="作者" e218edb10161 更改后的镜像名 删除镜像 docker rmi 49c614fbbea8 Error response from daemon: conflict: unable to delete 49c614fbbea8 (must be forced) - image is referenced in multiple repositories # 这个报错说明它被其他的镜像依赖,所以需要先删除依赖的image [root@FantJ ~]# docker rmi 49c614fbbea8 Error response from daemon: conflict: unable to delete 49c614fbbea8 (must be forced) - image is being used by stopped container ff...
- 下一篇
JDK动态代理源码解析
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34173549/article/details/80557290 分析版本jdk1.8 在分析jdk动态代理之前,先来了解java WeakReference弱引用的使用。运行期创建目标对象的代理非常耗时,使用缓存来存储生成的代理类显得尤为重要。jdk动态代理使用弱引用指向cache中的代理类,以便代理类对象能够被GC回收。 在java中,当一个对象O被创建时,它被放在Heap里,当GC运行的时候,如果发现没有任何引用指向O,O就会被回收以腾出内存空间,或者说,一个对象被回收,必须满足两个条件:1)没有任何引用指向它;2)GC被运行。在现实情况写代码的时候, 我们往往通过把所有指向某个对象的referece置空来保证这个对象在下次GC运行的时候被回收 (可以用java -verbose:gc来观察gc的行为)。 [java] view plain copy Objectc=newCar(); c=null; 但是, 手动置空对象对于程序员来说, 是一件繁琐且违背自动回收的理念的. ...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS6,CentOS7官方镜像安装Oracle11G
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- 设置Eclipse缩进为4个空格,增强代码规范
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS8编译安装MySQL8.0.19