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 ...
随机推荐
- struts笔记1
框架:所谓框架,就是把一些繁琐的重复性代码封装起来,使程序员在编码中把更多的精力放到业务需求的分析和理解上面 SHH:strust spring hibernate; SSM:springmvc sp ...
- Android中使用LitePal操控SQLite数据库
<第一行代码>读书手札 (一)什么是LitePal数据库 LitePal数据库是安卓的一个开源库,我们在以后的开发中,将会遇到许许多多的开源库,感谢开源社 区:因为开源社区的存在,一些我们 ...
- 长乐培训Day3
T1 奶牛晒衣服 题目 [题目描述] 在熊大妈英明的带领下,时针和他的同伴生下了许多牛宝宝.熊大妈决定给每个宝宝都穿上可爱的婴儿装.于是,为牛宝宝洗晒衣服就成了很不爽的事情. 圣人王担负起了这个重任. ...
- 链表习题(1)-设计一个递归算法,删除不带头结点的单链表L中所有值为x的结点
/*设计一个递归算法,删除不带头结点的单链表L中所有值为x的结点*/ /* 算法思想:设f(L,x)的功能是删除以L为首结点指针的单链表中所有值等于x的结点, 则显然有f(L->next,x)的 ...
- netty--处理器
编解码器的基类: 编码:MessageToByteEncode 解码:ByteToMessageDecoder
- 运输计划[二分答案 LCA 树上差分]
也许更好的阅读体验 \(\mathcal{Description}\) 原题链接 概括一下题意 给一颗有\(n\)个点带边权的树,有\(m\)个询问,每次询问\(u,v\)两点间的权值和,你可以将树中 ...
- myisam和innodb的区别,java事务不起作用原因
myisam:只支持表级锁.不支持事务.方便移植.该类型是mysql默认表存储类型 innodb:支持表级锁和行级锁.支持事务. 如果你的事务不起作用,很可能是用了myisam存储引擎,检查数据表引擎 ...
- Nginx 配置反向代理ip
参考文档: https://blog.csdn.net/stevenprime/article/details/7918094
- docker registry-v2 搭建私有仓库
参考官方文档:https://docs.docker.com/registry/deploying/ 参考 :http://www.tuicool.com/articles/6jEJZj 本例子使用两 ...
- 基于【 springBoot +springCloud+vue 项目】一 || 项目架构简介
一.前言 基于前期学习以及工作经验积累,持续更新基于springboot+springcloud+vue的demo项目.