JavaScript是如何工作的01:引擎,运行时和调用堆栈的概述!
概述
几乎每个人都已经听说过 V8 引擎,大多数人都知道 JavaScript 是单线程的,或者它使用的是回调队列。
在本文中,我们将详细介绍这些概念,并解释 JavaScrip 实际如何运行。通过了解这些细节,你将能够适当地利用所提供的 API 来编写更好的、非阻塞的应用程序。
如果您对JavaScript还比较陌生,那么本文将帮助您理解为什么JavaScript与其他语言相比如此“怪异”。
如果你是一个有经验的JavaScript开发人员,希望它能让您对每天使用的JavaScript运行时的实际工作方式有一些新的见解。
JavaScript引擎
JavaScript引擎的一个流行示例是Google的V8引擎。例如,在Chrome和Node.js中使用V8引擎,下面是一个非常简化的视图:

V8引擎由两个主要部件组成:
emory Heap(内存堆) — 内存分配地址的地方
Call Stack(调用堆栈) — 代码执行的地方
Runtime(运行时)
有些浏览器的 API 经常被使用到(比如说:setTimeout),但是,这些 API 却不是引擎提供的。那么,他们是从哪儿来的呢?事实上这里面实际情况有点复杂。

所以说我们还有很多引擎之外的 API,我们把这些称为浏览器提供 API 称为 Web API,比如说 DOM、AJAX、setTimeout等等。
然后我们还拥有如此流行的事件循环和回调队列。
调用栈
JavaScript是一种单线程编程语言,这意味着它只有一个调用堆栈。因此,它一次只能做一件事。
调用栈是一种数据结构,它记录了我们在程序中的位置。如果我们运行到一个函数,它就会将其放置到栈顶,当从这个函数返回的时候,就会将这个函数从栈顶弹出,这就是调用栈做的事情。
来个栗子:

当程序开始执行的时候,调用栈是空的,然后,步骤如下:

每一个进入调用栈的都称为调用帧。
这能清楚的知道当异常发生的时候堆栈追踪是怎么被构造的,堆栈的状态是如何的,让我们看一下下面的代码:

如果这发生在 Chrome 里(假设这段代码实在一个名为 foo.js 的文件中),那么将会生成以下的堆栈追踪:

"堆栈溢出",当你达到调用栈最大的大小的时候就会发生这种情况,而且这相当容易发生,特别是在你写递归的时候却没有全方位的测试它。我们来看看下面的代码:

当引擎开始执行这段代码时,它首先调用函数“foo”。然而,这个函数是递归的,并且在没有任何终止条件的情况下开始调用自己。因此,在执行的每一步中,相同的函数都会被一次又一次地添加到调用堆栈中,如下所示:

然而,在某些时候,调用堆栈中的函数调用数量超过了调用堆栈的实际大小,浏览器决定采取行动,抛出一个错误,它可能是这样的:

在单个线程上运行代码很容易,因为你不必处理在多线程环境中出现的复杂场景——例如死锁。
但是在一个线程上运行也非常有限制,由于 JavaScript 只有一个调用堆栈,当某段代码运行变慢时会发生什么?
并发与事件循环
当调用堆栈中的函数调用需要花费大量时间来处理时会发生什么情况? 例如,假设你希望在浏览器中使用JavaScript进行一些复杂的图像转换。
你可能会问-为什么这是一个问题?问题是,当调用堆栈有函数要执行时,浏览器实际上不能做任何其他事情——它被阻塞了,这意味着浏览器不能呈现,它不能运行任何其他代码,它只是卡住了,如果你想在应用中使用流畅的页面效果,这就会产生问题。
而且这不是唯一的问题,一旦你的浏览器开始处理调用栈中的众多任务,它可能会停止响应相当长一段时间。大多数浏览器都会这么做,报一个错误,询问你是否想终止 web 页面。

这并不是最好的用户体验,不是吗?
那么,我们怎样才能在不阻塞UI和不使浏览器失去响应的情况下执行大量代码呢?解决方案是异步回调。
本文内容来自微信公众号
JavaScript是如何工作的01:引擎,运行时和调用堆栈的概述!的更多相关文章
- JavaScript是如何工作的:引擎,运行时和调用堆栈的概述!
摘要: 理解JS执行原理. 原文:JavaScript是如何工作的:引擎,运行时和调用堆栈的概述! 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 本文是旨在深入研究JavaScrip ...
- JavaScript 对引擎、运行时、调用堆栈的概述理解
JavaScript 对引擎.运行时.调用堆栈的概述理解 随着JavaScript越来越流行,越来越多的团队广泛的把JavaScript应用到前端.后台.hybrid 应用.嵌入式等等领域. 这篇文 ...
- JavaScript是如何工作的:引擎,运行时间以及调用栈的概述
JavaScript是如何工作的:引擎,运行时以及调用栈的概述 原文:How JavaScript works: an overview of the engine, the runtime, and ...
- Java 内存管理、JVM 工作原理与 Java 运行时系统
Java 虚拟机规范中说明:所有的对象实例(all class instances)以及数组都要在堆上分配: the heap is the runtime data area from which ...
- gdb运行时结合汇编堆栈分析
一.从源代码文件到可执行文件 从C文件到可执行文件,一般来说需要两步,先将每个C文件编译成.o文件,再把多个.o文件和链接库一起链接成可执行文件.但具体来说,其实是分为四步,下面以ex ...
- Android webview 运行时不调用系统自带浏览器
WebView mobView = new WebView(this); mobView.loadUrl("http://www.csdn.net"); WebSettings w ...
- JavaScript 是如何工作的:JavaScript 的共享传递和按值传递
摘要: 原始数据类型和引用数据类型的副本作为参数传递给函数. 原文:JavaScript 是如何工作的:JavaScript 的共享传递和按值传递 作者:前端小智 Fundebug经授权转载,版权归原 ...
- JavaScript 是如何工作的:JavaScript 的内存模型
摘要: 从内存角度理解 let 和 const 的意义. 原文:JavaScript 是如何工作的:JavaScript 的内存模型 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 这 ...
- JavaScript是如何工作的:编写自己的Web开发框架 + React及其虚拟DOM原理
这是专门探索 JavaScript 及其所构建的组件的系列文章的第 19 篇. 如果你错过了前面的章节,可以在这里找到它们: JavaScript 是如何工作的:引擎,运行时和调用堆栈的概述! Jav ...
随机推荐
- k8s开发实践
代码自动生成:https://blog.openshift.com/kubernetes-deep-dive-code-generation-customresources/ 自定义controlle ...
- go语言实现单链表
线性表包含两种存储方法:顺序存储结构和链式存储结构,其中顺序表的缺点是不便插入与删除数据. 单链表:每个结点包含两部分:数据域+指针域,上一个结点的指针指向下一结点,依次相连,形成链表.特别注意的是每 ...
- Kubernetes(k8s) docker 修改 /dev/shm大小
一.问题 /dev/shm在/etc/fstab中挂载,对应tmpfs,实际使用的是内存的空间.默认情况下,/dev/shm为物理内存大小的一半. 在Kubernetes上跑docker,发现/dev ...
- 通过 Kubeadm 安装 K8S 与高可用,版本1.13.4
环境介绍: CentOS: 7.6 Docker: 18.06.1-ce Kubernetes: 1.13.4 Kuberadm: 1.13.4 Kuberlet: 1.13.4 Kuberctl: ...
- Java安装和环境配置
Java安装和环境配置 从事Java开发第一关就是安装JAVA环境. 我们要安装JDK, 全称Java开发全套. 其中包含了JRE(运行时环境), 如果你打游戏的时候可能会提示你缺少JRE. 我们要做 ...
- HTTP响应状态码整理
1xx:信息 100 Continue服务器仅接收到部分请求,但是一旦服务器并没有拒绝该请求,客户端应该继续发送其余的请求.101 Switching Protocols服务器转换协议:服务器将遵从客 ...
- ElementUI对话框(dialog)提取为子组件
需求:在页面的代码太多,想把弹窗代码提取为子组件,复用也方便. 这里涉及到弹窗el-dialog的一个属性show-close: show-close="false"是设置不显 ...
- laravel模型关联评论
用户模型 public function show(Post $post,LogManager $log){ $post->load("comments"); //这种方式是 ...
- Linux文件(夹)属性
ll 或者 ls -lh 查看文件属性:
- Spark学习笔记3——RDD(下)
目录 Spark学习笔记3--RDD(下) 向Spark传递函数 通过匿名内部类 通过具名类传递 通过带参数的 Java 函数类传递 通过 lambda 表达式传递(仅限于 Java 8 及以上) 常 ...