出自程序员之手的 JavaScript 代码, 该如何变成计算机所能理解的机器语言呢? 本文将带你走进 JavaScript 引擎内部, 一探究竟.
JavaScript 很酷(这一点不用我说), 但一台机器究竟是怎样理解我们编写的代码呢? 作为 JavaScript 开发者, 我们通常不需要处理编译器的东西. 但是, 了解 JavaScript 引擎的基础知识, 知道它如何将人类能看懂的 JS 代码变成机器能理解的东西, 是绝对是有好处的!
注意: 这篇文章主要根据 Node.JS 和基于 Chromium 的浏览器使用的 V8 引擎撰写.
当 html 解析器遇到代码中的 script 标签时, 就会从网络, 缓存或者已安装的 service worker 里加载源代码. 这一步的结果就是脚本内容, 以字节流的形式返回, 这个字节流需要解码器来处理! 字节流解码器会在字节流下载的时候进行解码.
字节流解码器根据流中的字节数据来创建符号(token). 例如, 0066 解码成 f,0075 解码成 u,006e 解码成 n,0063 解码成 c,0074 解码成 t,0069 解码成 i,006f 解码成 o,006e 解码成 n, 然后是一个空格. 似乎你写了一个 function! 这是 JavaScript 的保留关键字, 因此就会创建一个符号, 然后发给解析器(以及预解析器, 我的 GIF 图里没有说, 但我会稍后解释). 字节流中的其余内容也会类似处理.
引擎有两个解析器: 一个是预解析器(pre-parser), 另一个是解析器(parser). 预解析器只负责尽早检查符号, 找出其中的语法错误. 这样可以减少在代码中发现错误所需的时间. 否则这些错误就要由解析器负责发现了!
如果没有错误, 解析器就会根据它从字节流解码器收到的符号创建节点, 然后使用这些节点创建一颗抽象语法树, 简称 AST.
接下来就是解释器 (interpreter) 出场了! 解释器会遍历整个 AST, 根据 AST 的内容生成字节码. 字节码生成完成后, 就会删除 AST 以释放更多的内存. 这样就得到了机器能够运行的代码!
虽然字节码很快, 但它还可以更快. 字节码在运行的时候会生成信息. 它可以检测到哪些行为会更频繁发生, 哪些类型的数据会更经常被使用. 如果某个函数被调用了许多次, 那么就可以通过优化加快速度!
字节码会连同生成的类型反馈一起发送到优化编译器(optimizing compiler). 优化编译器会处理负责处理字节码和类型反馈, 然后生成高度优化过的机器码.
JavaScript 是一个动态类型语言, 这意味着数据类型经常会变化. 如果 JavaScript 引擎每次都必须检查值的类型, 那就会非常慢.
然而, JavaScript 的引擎使用了一种叫做内联缓存 (inline caching) 的方法. 它会在内存中缓存代码, 期待着以后会用同样的行为返回同样的值! 比如, 一个函数被调用 100 次, 到目前为止每次都返回同样的值. 那么引擎就会假设该函数在第 101 次调用时依然会返回同样的值.
我们假设有一个函数 sum, 到目前为止每次调用都使用两个数值作为参数:
上面的调用会返回 3! 下次被调用时, 引擎就会假设我们依然会用两个数值进行调用.
如果这个假设正确, 那就不需要进行动态查找, 可以直接使用内存中保存的值. 否则, 如果假设错误, 就会进行反优化, 将代码从优化过的机器码恢复成原始的字节码.
例如, 假设下次调用时传递了一个字符串而不是数值. 由于 JavaScript 是动态类型, 这样做不会产生任何错误!
这意味着数字 2 会被强制转换成字符串, 然后函数会返回字符串 "12". 因此引擎会去执行字节码, 然后更新类型反馈.
希望这篇文章对你有帮助性! 当然, 引擎还有许多其他方面我没有讨论到(如 JS heap,call stack 等), 也许以后会讨论! 如果你对 JavaScript 的内部原理有兴趣, 我强烈建议你自己做一些研究, V8 是开源的, 关于其工作原理的文档也非常好!
如果对于学习编程有很多疑惑, 没有思路, 不知道如何有效率的学习, 可以添加我的前端交流学习群 965747894, 需要最新系统的学习教程也可以管我要. 做了很多年开发, 对于学习方式, 如何提高自己的技术有一定的经验, 术业有专攻, 多跟有经验的人交流学习, 对这个行业信息了解的多, 职业发展的空间就越大
来源: http://www.jianshu.com/p/fb6ceeff8a30