前言
关于JavaScript引擎分析的文章后续会写一个系列,本文是该系列的第一篇,是关于JS引擎,运行时间和调用栈的部分。通过本系列文章的阅读,相信你对JavaScript引擎应该会有比较深入的理解。当然,电脑上怎么执行js代码,欢迎大家关注我,我将持续分享哪些前端层面核心的知识点,希望能给同处前端的你带来一点点收获。关于JavaScript引擎的理解也欢迎大家阅读我写的《》,《》,《》等等。
1、概述
随着JavaScript越来越流行,很多团队在他们的前端,后端,混合应用和嵌入式的技术栈中扩展或正在扩展JavaScript。
我们常见的执行js代码都是放入到HTML引入后然后通过HTML文件来执行胡查看代码。显然这是比较麻烦的事情,如果你的电脑里面安装了node.js,你可以使用node来直接使用node来运行你想要运行的js文件。
这篇文章是深入理解JavaScript及其工作原理系列的第一篇,通过理解JavaScript构建模块以及其如何工作将会帮助你写出更好的代码和应用。我们也会分享一些关于创建SessionStack时的经验,SessionStack一个轻量级,强壮高性能且时刻保持竞争性的JavaScript应用。
正如GitHut stats展示的那样,JavaScript是目前最活跃,最多push的语言。
如果项目目前非常依赖JavaScript,这意味着开发者必须深入理解JavaScript的内部原理然后才能使用JavaScript去构建好的软件。
几乎所有人都听过V8引擎这个概念,并且大多数人都知道JavaScript是单线程,同时它使用的是回调队列来处理异步操作。在这篇文章里,我们将解释JavaScript是如何运行的。通过知道这些细节,你将会写出更棒的代码。
如果你是一个有经验的JavaScript开发者了,那么希望这篇文章在JavaScript是如何运行这个方面能给你带来一点点新的思考。
2、JavaScript引擎
现在很流行的一个JavaScript引擎就是Google的V8引擎,他被用在Chrome和Node.js里面。这里有一张非常简单的概览图:
引擎由两个主要组件构成:
1.js是单线程的,他的加载分成两部分,第一个部分是预处理,主要是操作声明式函数,就是function aaa(){} 这类和变量,虽然说处理,也只是拿出来,并没有赋值,赋值是第二阶段运行的。2.如果要实现上述我们的目标,就。
Memory Heap(内存堆) —— 内存分配的地方
Call Stack(调用栈) —— 代码执行的地方
译者注:JS基础数据类型内存是在栈中分配,而对象类型其实是在堆中分配的。可以阅读上面说的《高手进阶之史上最全JS内存管理策略剖析》
3、JavaScript执行环境runtime
这里除了引擎之外还有更多,例如由浏览器提供的Web APIs,就像DOM,AJAX,setTimeout等等。同时,我们还有如此流行的事件循环和回调队列。
译者注:类似于ajax,setTimeout等其实是宿主环境提供的。ECMAScript对象类型主要有以下几种:
(1)本地对象:Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError
(2)内置对象:开发者不用明确实例化,只有Gblobal,Math对象
(3)宿主对象:如DOM,BOM等由宿主提供的对象
4、The Call Stack(调用栈)
JavaScript是一个单线程的编程语言,这意味着它有一个单独的调用栈,因此它只能在同一时间做一件事。
1、首先打开苹果手机,并进入浏览器主页。2、其次点击浏览器的脚本运行功能。3、最后导入js脚本,进行运行即可。
调用栈是一种描述我们程序进程的数据结构,如果我们执行一个函数,我们就把这个函数作用域相关的信息放在栈的顶部(称作栈帧),如果我们某一个函数执行结束了,那么我们就删除栈中的第一个栈帧。这就是栈做的事情。
让我们看个例子,请看下列代码:
function multiply(x,y) { return x * y;}function printSquare(x) { var s = multiply(x,x); console.log(s);}printSquare(5);
当引擎开始执行这段代码,Call Stack将会清空,并且开始如下步骤:
function foo() { throw new Error('SessionStack will help you resolve crashes :)');}function bar() { foo();}function start() { bar();}start();
如果这段代码(假设在一个叫做foo.js的文件内)放在Chrome里执行,那么会产生如下的调用栈错误信息。
"栈溢出" —— 这发生在所用内存达到了栈内存的最大值。这很容易发生,尤其是在你用一些不经测试的递归代码时。看一下下面的代码:
function foo() { foo();}foo();
然而,在某些时刻,当函数的调用所用栈内存超过了调用栈的实际大小时,浏览器会做抛出一个错误。
在单线程上运行代码会很简单,你不必处理那些多线程造成的复杂场景 —— 例如,死锁。
你必须先启用设置,才能从快捷指令运行 JavaScript。请参阅iPhone 或 iPad 上“快捷指令”中的高级隐私和安全性设置。
但是在单线程上运行也有限制。因为JavaScript只有一个单一的调用栈。
5、并发 & 时间循环
当调用栈中调用了非常耗时的函数时会发生什么呢?例如,想象你想用JavaScript在浏览器中做一些复杂的图像转换。
你可能会问 —— 这是个问题吗?在栈中执行一个会花费大量时间的函数,会导致浏览器被阻塞而做不了任何事。这意味着浏览器无法渲染页面,不能运行代码,它被卡住了。这将会严重影响你的应用的UI和使用体验。
并且这不是唯一的问题。一旦你的浏览器开始在调用栈内处理大量的任务,那么它就会停止响应很长一段时间。随后大多数浏览器会提示错误,询问你是否想要终止当前的页面。
按照这种情况,这可不是好的用户体验,对吗?那么我们怎么才能在执行耗时的代码的同时又不阻塞UI并且使浏览器保持响应呢?我们的解决方案是异步回调,关于异步为什么能防止浏览器假死可继续阅读《》,《》