为什么说函数是一等公民
因为函数能够当做参数或返回值进行传递。
动态类型语言
Javascript
语言的另一个重大特点就是,它是一种无类型语言,或者说是动态类型语言。相比较而言,C++或者 Java 等语言都是静态类型语言,它们在编译的时候就能够知道每个变量的类型。但是,JavaScript 的语言特性让我们没有办法在编译的时候知道变量的类型
,所以只能在运行的时候才能确定
,这导致 JavaScript 语言的规范面临着性能方面的巨大压力。在运行时计算和决定类型,会带来很严重的性能损失
,这导致了 JavaScript 语言的运行效率比 C++或者 Java 都要低很多。 ###为什么这样会比静态类语言性能差
因为静态类型语言在编译时,就已经知道了参数的类型,并根据类型生成了本地代码
;所以运行的时候从本地代码中读取就比较快。
而 javascript 是动态类型语言,只有当运行的时候才去编译、运行,上面的工作才开始做,这样效率当然会比较低了。
JavaScript 和 C++语言的区别:
- 编译确定位置:C++有明确的两个阶段,而编译这些位置的偏移信息都是编 译器在编译的时候就决定了的,当 C++代码编译成本地代码之后,对象的 属性和偏移信息都计算完成。
因为JavaScript没有类型,所以只有在对象创建的时候才有这些信息,因而只能在**执行阶段**确定,
而且 JavaScript 语言能够在执行时修改对象的属性(不是属性值,而是添加或者删除属性本身)。 - 偏移信息共享:C++因为有类型定义,所以所有对象都是按照该类型来确定 的,而且不能在执行的时候动态改变类型,因为这些对象都是共享偏移信息 的。访问它们只需要按照编译时确定的偏移量即可。而对于 C++模板的支 持,其实是多份代码,因为本质上其道理是相同的。
JavaScript则不同,每 个对象都是自描述,属性和位置偏移信息都包含在自身的结构中。
- 偏移信息查找:C++中查找偏移地址很简单,都是在编译代码时,对使用到 某类型的成员变量直接设置偏移量。而对于 JavaScript,使用到一个对象则需要通过
属性名匹配
才能查找到对应的值,这实在太费时间了。
JIT
推动 JavaScript 运行速度提高的另一大利器是 JIT (Just-In-Time)技术,它不是 一项全新的技术,其作用是解决解释性语言的性能问题,主要思想是当解释器将源 代码解释成内部表示的时候(Java 字节码就是一个典型例子),JavaScript 的执行环境不仅是解释这些内部表示,而且将其中一些字节码(主要是使用率高的部分)转成本地代码(汇编代码),这样可以被 CPU 直接执行,而不是解释执行,从而极大地提高性能。JIT 技术被广泛地使用在各种语言的执行环境中,例如 Java 虚拟机,经过长时间的演进之后,目前使用在 JavaScript 的众多引擎中,例如 JavaScriptCore、V8、SpiderMonkey 等中。
闭包
首先介绍一个学术解释,“闭包是一个拥有许多变量和绑定了这些变量的环境的 表达式(通常是一个函数),因而这些变量也是该表达式的一部分”。
通俗来说,就是当执行到一条语句的时候,哪些对象(或者其他环境因素)能够被使用。JavaScript 使用作用域链来实现闭包,作用域链由执行环境维护,JavaScript 中所有的标识符都是通过作用域链来查找值的。
JavaScript 引擎
什么是 JavaScript 引擎?简单来讲,就是能够将 JavaScript 代码处理并执行的运行环境。要解释这一概念,需要了解一些编译原理的基础概念和现代语言需要的一些新编译技术。
JavaScript 和 java 的区别:
其一是类型。JavaScript 是无类型的语言,其对于对象的表示和属性的访问比 Java 存在更大的性能损失。不过现在已经出现了一些新的技术,参考 C++或者 Java 的类型系统的优点,构建隐式的类型信息,这些后面将逐一介绍。
其二,Java 语言通常是将源代码编译成字节码,这同执行阶段是分开的,也就是从源代码到抽象语法树再到字节码这段时间的长短是无所谓的(或者说不是特别 重要),所以尽可能地生成高效的字节码即可。而对于 JavaScript 而言,这些都是在网页和 JavaScript 文件下载后同执行阶段
一起在网页的加载和渲染过程中来实施的, 所以对它们的处理时间也有着很高的要求。
JavaScript 引擎包括以下几个部分:
- 编译器。主要工作是将源代码编译成抽象语法树,在某些引擎中还包含将抽 象语法树转换成字节码。
- 解释器。在某些引擎中,解释器主要是接收字节码,解释执行这个字节码, 同时也依赖垃圾回收机制等。
- JIT 工具。一个能够能够 JIT 的工具,将字节码或者抽象语法树转换成本地 代码,当然它也需要依赖牢记
- 垃圾回收器和分析工具(Profiler )。它们负责垃圾回收和收集引擎中的信息,帮助改善引擎的性能和功效。
优化回滚
前面提到 V8 引擎为了性能上的优化,引入了更为高效的 Crankshaft 编译器。但 是为了性能考虑,该编译器通常会做比较乐观和大胆的预测,那就是编译器认为这 些代码比较稳定,变量类型不会发生改变,所以能够生成高效的本地代码。当然这 是理想情况,现实是引擎会发现一些变量的类型已经发生变化。在这种情况下,V8 使用一种机制来将它做的这些错误决定回滚到之前的一般情况,这个过程称为优化 回滚。
下面举个例子来说明为什么会出现这种情况吧。示例代码介绍了其中一种情况,函数 ABC 被调用很多次之后, VS 引擎可能会触发 Crankshaft 编译器来生成优化的代码,优化的代码认为示例代码的类型等信息都己经被获知了。但事实上,到目前为止,我们对于代码中的 unknown 变量的类型还一无所知,在这种情况下, VS 只能将该段代码回滚到一个通用的状态。
1 | var counter=0; |
优化回滚是一个很费时的操作,所以能够不回滚,肯定不要回滚,而且回滚会将之前优化的代码恢复到一个没有经过特别优化的代码,这是一个非常不高效的过程,写代码的时候要特别注意尽量不要触发这一过程。
《webkit 技术内幕》第九章