JavaScript数组的进化
JavaScript数组在内部实现上并不是连续的,而更类似于哈希映射或字典。这使得JavaScript数组有些像是一门B级语言,因为数组的实现方式并不合适。
为什么说JavaScript数组不是真正的数组
数组是一串连续的内存位置,用来保存某些值。重要的是要注意“连续”这个词。下图展示了数组在内存中的存储方式。一个包含4个元素的数组,每个元素占据4个字节,总共占用16个字节的内存空间。假设我们声明了一个tinyInt arr[4],该数组在内存中的地址从1201开始。当我们需要读取arr[2]时,只需要进行数学计算,即1201 (2 * 4),然后从1209开始读取即可。
JavaScript数组与真实数组的不同之处
JavaScript中的数据通过哈希映射实现,可以使用不同的数据结构,如链表。因此,如果在JavaScript中声明一个数组var arr new Array(4),计算机将生成类似上图的结构。如果程序需要读取arr[2],则需要从1201开始遍历寻址。很明显,数学计算比遍历链表快,尤其对于较长的数组而言。这就是JavaScript数组与真实数组的区别。
JavaScript数组的进化
JavaScript语言本身也在不断演化。从V8、SpiderMonkey到TC39以及日益增长的Web用户群体,巨大的努力使JavaScript成为全球必备技术。当用户基数庞大时,性能提升变得非常重要。
现代的JavaScript引擎会为数组分配连续的内存,前提是数组是同质的(所有元素类型相同)。优秀的程序员通常会保证数组的同质性,以便JIT(即时编译器)能够使用C编译器风格的方法来读取元素。如果代码编写得不太糟糕,JavaScript Array对象在幕后仍然以真正的数组形式存在,这对现代的JavaScript开发者来说非常重要。
随着ES2015/ES6的推出,数组也有了更多的演进。TC39决定引入类型化数组(Typed Arrays),于是我们有了ArrayBuffer。ArrayBuffer提供一块连续的内存,可以随意操作。直接操作内存可能过于复杂和底层,因此产生了处理ArrayBuffer的视图(View)。目前已经有一些可用的视图,未来还会有更多加入。
了解更多关于类型化数组的知识
如果想要了解更多关于类型化数组(Typed Arrays)的知识,请访问MDN文档。高性能、高效率的类型化数组在WebGL之后被引入。WebGL开发者面临着处理二进制数据的性能问题。使用SharedArrayBuffer可以在多个Web Worker进程之间共享数据,提升性能。
旧式数组 vs 类型化数组:性能
前面已经探讨了JavaScript数组的演进,现在我们来测试现代数组带来的性能收益有多大。
旧式数组和ArrayBuffer的性能几乎相同,现代编译器已经足够智能,可以将元素类型相同的传统数组在内部转换成内存连续的数组。第一个例子就是这样。尽管使用了new Array(LIMIT),数组实际上仍然以现代数组的形式存在。
接下来,我们将修改第一个例子,将数组改为异构型(元素类型不完全一致),看看是否存在性能差异。
修改发生在第3行,添加一条语句将数组变为异构类型。其余代码保持不变。结果显示性能差异明显,慢了22倍。
类型化数组的引入
类型化数组的引入是JavaScript发展历程中的一大步。Int8Array、Uint8Array、Uint8ClampedArray、Int16Array、Uint16Array、Int32Array、Uint32Array、Float32Array、Float64Array等都是类型化数组的视图,它们使用原生字节序(与本机相同)。我们还可以使用DataView创建自定义的视图窗口。希望未来会有更多的DataView库帮助我们轻松操作ArrayBuffer。
JavaScript数组的演进非常不错。现在它们速度快、效率高、健壮,而且在内存分配方面也足够智能。
版权声明:本文内容由互联网用户自发贡献,本站不承担相关法律责任.如有侵权/违法内容,本站将立刻删除。