我们应该如何去了解JavaScript引擎的工作原理
1. 什么是JavaScript解析引擎?
简单地说,JavaScript解析引擎就是能够“读懂”JavaScript代码,并准确地给出代码运行结果的一段程序。比方说,当你写了 var a = 1 + 1; 这样一段代码,JavaScript引擎做的事情就是看懂(解析)你这段代码,并且将a的值变为2。
学过编译原理的人都知道,对于静态语言来说(如Java、C++、C),处理上述这些事情的叫编译器(Compiler),相应地对于JavaScript这样的动态语言则叫解释器(Interpreter)。这两者的区别用一句话来概括就是:编译器是将源代码编译为另外一种代码(比如机器码,或者字节码),而解释器是直接解析并将代码运行结果输出。比方说,firebug 的console就是一个JavaScript的解释器。
但是,现在很难去界定说,JavaScript引擎它到底算是个解释器还是个编译器,因为,比如像V8(Chrome的JS引擎),它其实为了提高JS的运行性能,在运行之前会先将JS编译为本地的机器码(native machine code),然后再去执行机器码(这样速度就快很多),相信大家对JIT(Just In Time Compilation)一定不陌生吧。
我个人认为,不需要过分去强调JavaScript解析引擎到底是什么,了解它究竟做了什么事情我个人认为就可以了。对于编译器或者解释器究竟是如何看懂代码的,翻出大学编译课的教材就可以了。
这里还要强调的就是,JavaScript引擎本身也是程序,代码编写而成。比如V8就是用C/C++写的。
2. JavaScript解析引擎与ECMAScript是什么关系?
JavaScript引擎是一段程序,我们写的JavaScript代码也是程序,如何让程序去读懂程序呢?这就需要定义规则。比如,之前提到的var a = 1 + 1;,它表示:
•左边var代表了这是申明(declaration),它申明了a这个变量•右边的+表示要将1和1做加法
•中间的等号表示了这是个赋值语句
•最后的分号表示这句语句结束了
上述这些就是规则,有了它就等于有了衡量的标准,JavaScript引擎就可以根据这个标准去解析JavaScript代码了。那么这里的
ECMAScript就是定义了这些规则。其中ECMAScript 262这份文档,就是对JavaScript这门语言定义了一整套完整的标准。其中包括:
•var,if,else,break,continue等是JavaScript的关键词
•abstract,int,long等是JavaScript保留词
•怎么样算是数字、怎么样算是字符串等等
•定义了操作符(+,-,>,<;等)
•定义了JavaScript的语法
•定义了对表达式,语句等标准的处理算法,比如遇到==该如何处理
•⋯⋯
标准的JavaScript引擎就会根据这套文档去实现,注意这里强调了标准,因为也有不按照标准来实现的,比如IE的JS引擎。这也是为什么JavaScript会有兼容性的问题。至于为什么IE的JS引擎不按照标准来实现,就要说到浏览器大战了,这里就不赘述了,自行Google之。
所以,简单的说,ECMAScript定义了语言的标准,JavaScript引擎根据它来实现,这就是两者的关系。
3. JavaScript解析引擎与浏览器又是什么关系?
简单地说,JavaScript引擎是浏览器的组成部分之一。因为浏览器还要做很多别的事情,比如解析页面
、渲染页面、Cookie管理、历史记录等等。那么,既然是组成部分,因此一般情况下JavaScript引擎都是浏览器开发商自行开发的。比如:IE9的Chakra、Firefox的TraceMonkey、Chrome的V8等等。
从而也看出,不同浏览器都采用了不同的JavaScript引擎。因此,我们只能说要深入了解哪个JavaScript引擎。
4. 深入了解其内部原理的途径有哪些?
搞清楚了前面三个问题,那这个问题就好回答了。个人认为,主要途径有如下几种(依次由浅入深):
•看讲JavaScript引擎工作原理的书
这种方式最方便,不过我个人了解到的这样的书几乎没有,但是
Dmitry A.Soshnikov博客上的文章真的是非常的赞。
•看ECMAScript的标准文档
这种方式相对直接,原汁原味,因为引擎就是根据标准来实现的。
目前来说,可以看第五版和第三版,不过要看懂也是不容易的。
•看JS引擎源代码
这种方式最直接,当然也最难了。因为还牵涉到了如何实现词法分
析器,语法分析器等等更加底层的东西了,而且并非所有的引擎代
码都是开源的。
5. 以上几种方式中第一种都很难看明白怎么办?
其实第一种方式中的文章,作者已经将文档中内容提炼出来,用通俗易懂的方式阐述出来了。如果,看起来还觉得吃力,那说明还缺少两块的东西:
•对JavaScript本身还理解的不够深入
如果你刚刚接触JavaScript,或者说以前甚至都没有接触过。那一
下子就想要去理解内部工作原理,的确是很吃力的。首先应该多看
看书,多实践实践,从知识和实践的方式来了解JavaScript预言
特性。这种情况下,你只需要了解现象。比方说,(function(){})()这
样可以直接调用该匿名函数、用闭包可以解决循环中的延迟操作的
变量值获取问题等等。要了解这些,都是需要多汲取和实践的。实践这里就不多说了,而知识汲取方面可以多看看书和博客。这个层
面的书就相对比较多了,《Professional JavaScript for Web
Developers》就是本很好的书(中文版请自行寻)。
•缺乏相应的领域知识
当JavaScript也达到一定深度了,但是,还是看不大明白,或者
没法很深入到内部去一探究竟。那就意味着缺少对应的领域知识。
这里明显的就是编译原理相关的知识。不过,其实对这块了解个大
概基本看起来就没问题了。要再继续深入,那需要对编译原理了解
的很深入,比如说词法分析采用什么算法,一般怎么处理。会有什
么问题,如何解决,AST生成算法一般有哪几种等等。那要看编
译原理方面的书,也有基本经典的书,比如《Compilers: Principles, Techniques, and Tools》这本也是传说中的龙书,还有非常著名的
《SICP》和《PLAI》。不过其实根据个人经验,对于Dmitry的
文章,要看懂它,只要你对JavaScript有一定深度的了解,同时
你大学计算机的课程都能大致掌握了(尤其是操作系统),也就是
说基础不错,理解起来应该没问题。因为这些文章基本没有涉及底
层编译相关的,只是在解释文档的内容,并且其中很多东西都是相
通的,比如:context的切换与CPU的进程切换、函数相关的的局
部变量的栈存储、函数退出的操作等等都是一致的。
以上就是个人对这个问题的看法,除此之外,我觉得,学习任何技术都不能操之过急,要把基础打扎实了,这样学什么都会很快。
编写可维护的代码的重要性
软件bug的修复是昂贵的,并且随着时间的推移,这些bug的成本也会增加,尤其当这些bug潜伏并慢慢出现在已经发布的软件中时。当你发现bug 的时候就立即修复它是最好的,此时你代码要解决的问题在你脑中还是很清晰的。否则,你转移到其他任务,忘了那个特定的代码,一段时间后再去查看这些代码就需要:
•花时间学习和理解这个问题
•花时间是了解应该解决的问题代码
还有问题,特别对于大的项目或是公司,修复bug的这位伙计不是写代码的那个人(且发现bug和修复bug的不是同一个人)。因此,必须降低理解代码花费的时间,无论是一段时间前你自己写的代码还是团队中的其他成员写的代码。这关系到底线(营业收入)和开发人员的幸福,因为我们更应该去开发新的激动人心的事物而不是花几小时几天的时间去维护遗留代码。
另一个相关软件开发生命的事实是,读代码花费的时间要比写来得多。有时候,当你专注并深入思考某个问题的时候,你可以坐下来,一个下午写大量的代码。
你的代码很能很快就工作了,但是,随着应用的成熟,还会有很多其他的事情发生,这就要求你的进行进行审查,修改,和调整。例如:
•bug是暴露的
•新功能被添加到应用程序
•程序在新的环境下工作(例如,市场上出现新的浏览器)
•代码改变用途
javascript免费教程•代码得完全从头重新,或移植到另一个架构上或者甚至使用另一种语言
由于这些变化,很少人力数小时写的代码最终演变成花数周来阅读这些代码。这就是为什么创建可维护的代码对应用程序的成功至关重要。
可维护的代码意味着:
•可读的
•一致的
•可预测的
•看上去就像是同一个人写的
•已记录
谨慎使用全局变量
全局对象的概念
JavaScript通过函数管理作用域。在函数内部声明的变量只在这个函数内部可用,而在函数外面不可用。另一方面,全局变量就是在任何函数外面声明的或是未声明直接简单使用的。
每个JavaScript环境有一个全局对象,当你在任意的函数外面使用this的时候可以访问到。你创建的每一个全局变量都成了这个全局对象的属性。在浏览器中,方便起见,该全局对象有个附加属性叫做window,此window(通常)指向该全局对象本身。下面的代码片段显示了如何在浏览器环境中创建和访问的全局变量:
谨慎使用全局变量
全局变量的问题在于,你的JavaScript应用程序和web页面上的所有代码都共享了这些全局变量,他们住在同一个全局命名空间,所以当程序的两个不同部分定义同名但不同作用的全局变量的时候,命名冲突在所难免。
web页面包含不是该页面开发者所写的代码也是比较常见的,例如:第三方的JavaScript库、广告方的脚本代码、第三方用户跟踪和分析脚本代码、不同类型的小组件/标志/按钮。
比方说,该第三方脚本定义了一个全局变量,叫做result;接着,在你的函数中也定义一个名为result的全局变量。其结果就是后面的变量覆盖前面的,第三方脚本就一下子嗝屁啦!因此,要想和其他脚本成为好邻居的话,尽可能少的使用全局变量是很重要的。一些减少全局变量的策略,例如命名空间模式或是函数立即自动执行,但是要想让全局变量少最重要的还是始终使用var来声明变量。
由于JavaScript的两个特征,不自觉地创建出全局变量是出乎意料的容易。首先,你可以甚至不需要声明就可以使用变量;第二,JavaScript 有隐含的全局概念,意味着你不声明的任何变量都会成为一个全局对象属性。参考下面的代码:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。