虚妄

JavaScript是如何工作的:引擎、运行时与调用栈

    笔记     JavaScript

  1. 1. 综述
  2. 2. JavaScript引擎
  3. 3. 运行时
  4. 4. 调用栈
    1. 4.1. 异常
    2. 4.2. 递归调用
  5. 5. 并发与事件循环

综述

随着Nodejs的出现,JavaScript就变得无处不在。前端、后端、混合App、嵌入式设备等。在Github上,JavaScript标签的项目也是逐年增长,伴随着ES2015标准的发布,JavaScript也变得现代起来。作为一名开发者,不仅要知道JavaScript的基础语法,同时也由必要了解JavaScript是如何运行的,这里面都牵扯到哪些东西。知道其背后的运行原理,有利于深刻了解我们平常写的代码,同时更好地利用现有的API

JavaScript引擎

目前最受欢迎的就是Google的V8引擎了。V8被用在了Google ChromeNode.js中。下面的图形简单展示了JavaScript引擎的基本工作:
Engine

引擎主要负责两部分:

  • 内存管理:也就是内存的分配与释放
  • 调用栈管理:也就是执行代码时的栈帧管理

运行时

在日常开发中,我们还会一些其他的API,比如setTimeout,这些API都是执行在浏览器环境中,这些API自然不是JavaScript引擎提供的。这些都称之为Web API,是由浏览器提供的,除了setTimeout,还有操作DOM的,AJAX等。除去这些还有用来相应DOM事件的事件循环回调队列
browser

调用栈

JavaScript的执行是单线程的,这意味这它只有一个调用栈。调用栈说白了就是数据结构,用来记录当前代码执行的一些信息。比如说下面的代码:

1
2
3
4
5
6
7
8
function multiply(x, y) {
return x * y;
}
function printSquare(x) {
var s = multiply(x, x);
console.log(s);
}
printSquare(5);

当上述的代码开始执行时,调用栈是空的。之后开始调用printSquare,接着multiply,接着console,然后开始出栈。形象化表示是这样的:
CallStack

调用栈中的每个条目称之为栈帧(Stack Frame)

这里包含了两个情况:

  • 异常
  • 递归调用

异常

1
2
3
4
5
6
7
8
9
10
function foo() {
throw new Error('SessionStack will help you resolve crashes :)');
}
function bar() {
foo();
}
function start() {
bar();
}
start();

如果上述代码运行在Chrome中的话,我们可以在Chrome Del ToolsConsole面板中发现会输出一行错误:
exception

上面的错误直接反映了调用栈的结构。

递归调用

递归调用直接导致的后果就是栈溢出,也就是超过了最大栈大小。

1
2
3
4
function foo() {
foo();
}
foo();

上述代码如果在Chrome中,会发现进程会卡死一段时间,然后在Chrome Del ToolsConsole面板中出现错误:
stack

调用栈形象化表示是这样的:
stack

并发与事件循环

因为JavaScript是单线程,所以如果要执行一个耗时的操作,比如复杂的图片转换,就会导致页面出现卡顿现象。此时就无法相应界面的任何操作,导致了非常不好的用户体验。甚至有时会出现以下的提示:
stuck

解决这个问题的办法就是使用异步回调(asynchronous callbacks)

这个将在下一篇详细解释。

页阅读量:  ・  站访问量:  ・  站访客数: