跳到主要内容

底层

解释型语言和编译型语言

他们都要转化成二进制代码才能执行

  1. 从运行效果上来看。编译型语言要等全部写完后通过编译器去生成一个二进制文件。 而解释型语言并没生成文件,而是直接生成效果.

  2. 运行的时候是否需要编译器的伴随,编译型语言运行的是最终生成的二进制代码,所以不需要编译器伴随。 而解释型语言则一边解释一边运行,所以运行的时候很可能还有部分代码没有解释好,所以需要编译器伴随 (解释型语言把该工具叫做解释器)。

  3. 执行的速度对比。编译型语言运行的已经是完全的二进制内容,运行起来十分干净利落,所以速度很快。 而解释型语言运行的不一定是完全的二进制内容,因为它是一边解释成二进制一边运行,速度较慢

4.可移植性对比。编译型语言是运行二进制内容,所以一旦 CPU 指令系统改变,那么之前的二进制文件可能运行不了。比如,如果到其他硬件平台上运行,就可能出现错误,就需要根据该平台重新编译出新的二进制文件。所以可移植性、平台兼容性比价差。而解释型语是在需要的时候才开始编译、运行。所以它自然具有可移植性,即在任何平台都可以马上运行起来。

编译

JavaScript 通常被认为是一种解释型语言,但现代 JavaScript 引擎不再只是解释 JavaScript,而是编译它。

这种情况从 2009 年就开始了,当时 Firefox 3.5 中添加了 SpiderMonkey JavaScript 编译器,每个人都遵循了这个想法。

JavaScript 由 V8 内部使用 即时 (JIT) 编译 编译以加快执行速度。

这似乎违反直觉,但自从 2004 年推出 Google Maps 以来,JavaScript 已经从一种通常执行几十行代码的语言发展成为在浏览器中运行数千到数十万行代码的完整应用。

我们的应用现在可以在浏览器中运行数小时,而不仅仅是一些表单验证规则或简单的脚本。

在这个新世界中,编译 JavaScript 非常有意义,因为虽然准备好 JavaScript 可能需要多一点时间,但一旦完成,它的性能将比纯解释代码高得多

快慢数组

V8 当中

我们可以看到 JSArray 是继承⾃ JSObject 的,所以在 JavaScript 中,数组可以是⼀个特殊的对 象,内部也是以 key-value 形式存储数据,所以 JavaScript 中的数组可以存放不同类型的值。

JSArray 继承于 JSObject ,从注释上看,它有两种存储⽅式:

fast 模式下数组在源码⾥⾯叫 FastElements ,⽽ slow 模式下的叫做 SlowElements 。

快数组(FastElements)

fast:存储结构是 FixedArray ,并且数组⻓度 <= elements.length() , push 或 pop
时可能会伴随着动态扩容或减容

FixedArray 是 V8 实现的⼀个类似于数组的类,它表示⼀段连续的内存,可以使⽤索引直接定位。 新创建的空数组默认就是快数组。当数组满(数组的⻓度达到数组在内存中申请的内存容量最⼤值) 的时候,继续 push 时, JSArray 会进⾏动态的扩容,以存储更多的元素。

慢数组(SlowElements)

slow:存储结构是 HashTable (哈希表),并且数组下标作为 key

慢数组以哈希表的形式存储在内存空间⾥,它不需要开辟连续的存储空间,但需要额外维护⼀个哈希 表,与快数组相⽐,性能相对较差。

所以,当处于以下情况时,快数组会被转变为慢数组: 当加⼊的索引值 index ⽐当前容量 capacity 差值⼤于等于 1024 时(index - capacity >= 1024) 快数组新容量是扩容后的容量 3 倍之多时 例如:向快数组⾥增加⼀个⼤索引同类型值 当往 arr 增加⼀个 2000 的索引时, arr 被转成慢数组。节省了⼤量的内存空间(从索引为 2 到 索引为 2000)。(不想在连续空间上存储了,中间部分会被浪费掉)

当慢数组的元素可存放在快数组中且⻓度在 smi 之间且仅节省了 50%的空间,则会转变为快数组

快数组的扩容(使用 push 时) 默认空数组初始化⼤⼩为 4 : 即⽼的容量的 1.5 倍加上 16 。初始化为 4 个,当 push 第 5 个的时候,容量将会变成:22 接着申请⼀块这么⼤的内存,把⽼的数据拷过去,把新元素放在当前 length 位置,然后将数组的 length + 1,并返回 length。

减容(使用 pop 时)可以分为以下⼏步: pop 操作时,获取数组 length 获取 length - 1 上的元素(要删除的元素) 数组 length - 1 判断数组的总容量是否⼤于等于 length - 1 的 2 倍 是的话,使⽤ RightTrimFixedArray 函数,计算出需要释放的空间⼤⼩,并做好标记,等待 GC 回收 不是的话,⽤ holes 对象填充 返回要删除的元素

JavaScript 中, JSArray 继承⾃ JSObject ,或者说它就是⼀个特殊的对象,内部是以 key-value 形式存储数据,所以 JavaScript 中的数组可以存放不同类型的值

  • 它有两种存储⽅式,快数组与慢数组,初始化空数组时,使⽤快数组,快数组使⽤连续的内存空间,当数组⻓度达到最⼤ 时, JSArray 会进⾏动态的扩容,以存储更多的元素,相对慢数组,性能要好得多。

  • 当数组中 hole 太多时,会转变成慢数组,即以哈希表的⽅式( key-value 的形式)存储数据,以节省内存空 间。