译者注
翻译一个对新手比较友好的 JavaScript 工作原理解析系列文章
注意: 以下全部是概念,经验丰富的老鸟可以离场啦
正文从这里开始
随着 javascript 的流行,团队们正在利用javascript来支持多个级别的技术栈,包括前端,后端,混合开发,嵌入式设备,以及更多
这篇文章旨在成为深入挖掘JavaScript和实际上他是怎么工作的
系列文章中的第一篇:我们通过知道javascript的模块(Building blocks)和他们如何组合在一起工作来写更好的代码和应用.我们还会分享一些我们构建sessionStarck(这是一款主打反馈功能的产品)时的经验法则,一款轻量级的javascript应用为了保持竞争力,必须时要健壮和高性能.
据githut stats(这是一个统计网站,根据gihub的数据来进行语言统计)的数据来看 , JavaScript 是github 上 活跃库最多的,和提交数最多的语言,这让他不会落后于其他类别.
如果项目变得如此依赖于JavaScript,这就意味着,为了开发惊艳的软件(可以理解为程序),开发者不得不利用这个语言和生态系统(JavaScript生态)提供的一切,对内部的更深入和更深入的了解.
事实证明,有很大一部分的开发者每天都在使用javascript,但是却不知道javascript 在底层干了啥(原文很长,其实就是这个意思,英文还真的是...)
Overview
基本上每个人都知道 v8 引擎这个概念了,大多数人知道javascript 是一个单线程语言 或者是那个使用回调队列的语言.
这篇文章,我们将跑通哪些概念的细节和说明javascript是如何运行的,通过知道这些细节,你能够正确的使用提供的api书写更好的,非阻塞的应用.
如果你是一个javascript新手,这篇文章能让你知道为什么javascript对比与其他语言,为什么如此神奇.
如果你是一个有经验的javascript开发者,我也希望如此,这会给你一些关于javascript运行时是怎么工作的闪亮的灵感(或者说新的见解).
javascript引擎
google v8 引擎是一个了流行的例子,nodejs 和 chrome 都是使用这个引擎,这里是一个简单的他看起来是什么的图
这个引擎看起来像是两个组件.
- memory Heap: 这是内存分配的位置
- call Stack: 这是你的代码执行时,堆栈帧(starck frame)的位置
运行时
在浏览器中有一些api已经被几乎所有的javascript开发者使用了,例如 setTimeout 这些api,然而,他们不是又引擎提供的.
那么,他们从哪里来的呢?
其实这有点复杂.
看,我们有引擎,但是其实我们还有很多东西.我们有那些浏览器提供的web apis,例如 DOM, AJAX, setTimeout等等.
而且,我们还有流行的事件循环(even loop)和回调队列(callback queue)
调用栈(回调队列跟调用栈其实意思差不多,不过栈跟队列是两种不同的数据结构)
javascript是一个单线程的编程语言(repeat又repeat,都说几次了),这意味着他有一个调用栈,因此,他一次只能做一件事情.
调用栈是一个记录了我们在程序中的位置的数据结构,如果我们跳进一个function,我们把这个函数放进栈的顶部(栈是一种先进后出的数据结构),如果我们从function中return出来,我们就从栈的顶部跳了出来,这就是栈能做的事情.
让我们来看一个例子:
function multiply(x, y) {
return x * y;
}
function printSquare(x) {
var s = multiply(x, x);
console.log(s);
}
printSquare(5);
当引擎执行这段代码的时候,调用栈(call stack)是空的,当进入printSquare的时候,栈上添加了一个函数,在printSquare中我们又进入了multiply中,此时栈的顶部又添加了一个函数,当我们从multiply中return的时候,栈就把顶部的函数弹出,此时我们就回到了printSquare里,然后执行完printSquare后引擎自动return undefined 以结束这个函数的执行.
栈的每一次变化就想下面这样:
栈中的每一个条目(entry)叫坐堆栈帧(stack frames)上面有提到
这就是一个异常抛出时,栈追踪是如何被构造的(how stack traces are being constructed)---这取决于异常发生的时候,回调栈的状态.(突然跑异常去了,其实是想说明,异常就是通过调用栈实现的)
function foo() {
throw new Error('SessionStack will help you resolve crashes :)');
}
function bar() {
foo();
}
function start() {
bar();
}
start();
如果这段代码在chrome执行,会产生下边的栈追踪(其实就是一个错误)
"栈坏了"(blowing the stack) --- 这发生在当你把栈放满了的时候(下面还说了一大推,还贴了代码,其实就是死递归)
当引擎执行死递归的时候,会不停的调用同一个方法.看起来像下面这样.
然而,函数在调用栈上调用的数量超过了调用栈的实际大小,浏览器决定要采取行动了,所以他抛出了一个错误,看起来是这样的
在单线程上运行代码可以很容易,因为你不用去处理多线程中的复杂场景,例如,死锁.
但是,运行在单线程上也有他的限制,由于javascript只有一个调用栈,若是程序执行得很慢怎么办?
并发和事件循环(even loop)
当你有函数调用在调用栈(call stack)里为了一个任务花费了大额的时间会发生什么?例如,想象一下,你要在浏览器了做一个复杂的图片转性(transfromation).
你可能会问--为什么这是一个问题?问题是调用栈有函数在运行,浏览器就不能做其他的事情,这就造成了阻塞,这意味这浏览器不能渲染,它不能运行任何的其他代码,它卡住了,如果你想你的app 的ui界面流畅,那么这就是一个问题.
然而,这不是唯一的问题,一旦你的浏览器在调用栈开始了很多的任务,这可能会在很长的一段时间内失去响应.而很多的浏览器会抛出一个错误,然后问你是否要关闭网页.
现在,这不是一个最好的用户体验,对吧?
那么,我们要如何处理这种需要很长时间执行的代码呢?嗯~,解决办法就是异步回调
这会再第二篇文章中详细说明.
下面开始买他们产品的广告了,就不翻译了.
原文第二篇我会尽快翻译,尽量不拖太久.
写作新手,还望大家多多关注,多多点赞.