JavaScript 数组的进化与性能分析


Posted in Javascript onSeptember 18, 2017

正式开始前需要声明,本文并不是要讲解 JavaScript 数组基础知识,也不会涉及语法和使用案例。本文讲得更多的是内存、优化、语法差异、性能、近来的演进。

在使用 JavaScript 前,我对 C、C++、C# 这些已经颇为熟悉。与许多 C/C++ 开发者一样,JavaScript 给我的第一印象并不好。

Array 是主要原因之一。JavaScript 数组不是连续(contiguous)的,其实现类似哈希映射(hash-maps)或字典(dictionaries)。我觉得这有点像是一门 B 级语言,数组实现根本不恰当。自那以后,JavaScript 和我对它的理解都发生了变化,很多变化。

为什么说 JavaScript 数组不是真正的数组

在聊 JavaScript 之前,先讲讲 Array 是什么。

数组是一串连续的内存位置,用来保存某些值。注意重点,“连续”(continuous,或 contiguous),这很重要。

JavaScript 数组的进化与性能分析

上图展示了数组在内存中存储方式。这个数组保存了 4 个元素,每个元素 4 字节。加起来总共占用了 16 字节的内存区。

假设我们声明了 tinyInt arr[4];,分配到的内存区的地址从 1201 开始。一旦需要读取 arr[2],只需要通过数学计算拿到 arr[2] 的地址即可。计算 1201 + (2 X 4),直接从 1209 开始读取即可。

JavaScript 数组的进化与性能分析

JavaScript 中的数据是哈希映射,可以使用不同的数据结构来实现,如链表。所以,如果在 JavaScript 中声明一个数组 var arr = new Array(4),计算机将生成类似上图的结构。如果程序需要读取 arr[2],则需要从 1201 开始遍历寻址。

以上急速 JavaScript 数组与真实数组的不同之处。显而易见,数学计算比遍历链表快。就长数组而言,情况尤其如此。

JavaScript 数组的进化

不知你是否记得我们对朋友入手的 256MB 内存的电脑羡慕得要死的日子?而今天,8GB 内存遍地都是。

与此类似,JavaScript 这门语言也进化了不少。从 V8、SpiderMonkey 到 TC39 和与日俱增的 Web 用户,巨大的努力已经使 JavaScript 成为世界级必需品。一旦有了庞大的用户基础,性能提升自然是硬需求。

实际上,现代 JavaScript 引擎是会给数组分配连续内存的 —— 如果数组是同质的(所有元素类型相同)。优秀的程序员总会保证数组同质,以便 JIT(即时编译器)能够使用 c 编译器式的计算方法读取元素。

不过,一旦你想要在某个同质数组中插入一个其他类型的元素,JIT 将解构整个数组,并按照旧有的方式重新创建。

因此,如果你的代码写得不太糟,JavaScript Array 对象在幕后依然保持着真正的数组形式,这对现代 JS 开发者来说极为重要。

此外,数组跟随 ES2015/ES6 有了更多的演进。TC39 决定引入类型化数组(Typed Arrays),于是我们就有了 ArrayBuffer。

ArrayBuffer 提供一块连续内存供我们随意操作。然而,直接操作内存还是太复杂、偏底层。于是便有了处理 ArrayBuffer 的视图(View)。目前已有一些可用视图,未来还会有更多加入。

var buffer = new ArrayBuffer(8);
var view  = new Int32Array(buffer);
view[0] = 100;

了解更多关于类型化数组(Typed Arrays)的知识,请访问 MDN 文档。

高性能、高效率的类型化数组在 WebGL 之后被引入。WebGL 工作者遇到了极大的性能问题,即如何高效处理二进制数据。另外,你也可以使用 SharedArrayBuffer 在多个 Web Worker 进程之间共享数据,以提升性能。

从简单的哈希映射到现在的 SharedArrayBuffer,这相当棒吧?

旧式数组 vs 类型化数组:性能

前面已经讨论了 JavaScript 数组的演进,现在来测试现代数组到底能给我们带来多大收益。下面是我在 Mac 上使用 Node.js 8.4.0 进行的一些微型测试结果。

旧式数组:插入

var LIMIT = 10000000;
var arr = new Array(LIMIT);
console.time("Array insertion time");
for (var i = 0; i < LIMIT; i++) {
arr[i] = i;
}
console.timeEnd("Array insertion time");

用时:55ms

Typed Array:插入
var LIMIT = 10000000;
var buffer = new ArrayBuffer(LIMIT * 4);
var arr = new Int32Array(buffer);
console.time("ArrayBuffer insertion time");
for (var i = 0; i < LIMIT; i++) {
arr[i] = i;
}
console.timeEnd("ArrayBuffer insertion time");

用时:52ms

擦,我看到了什么?旧式数组和 ArrayBuffer 的性能不相上下?不不不。请记住,前面提到过,现代编译器已经智能化,能够将元素类型相同的传统数组在内部转换成内存连续的数组。第一个例子正是如此。尽管使用了 new Array(LIMIT),数组实际依然以现代数组形式存在。

接着修改第一例子,将数组改成异构型(元素类型不完全一致)的,来看看是否存在性能差异。

旧式数组:插入(异构)
var LIMIT = 10000000;
var arr = new Array(LIMIT);
arr.push({a: 22});
console.time("Array insertion time");
for (var i = 0; i < LIMIT; i++) {
arr[i] = i;
}
console.timeEnd("Array insertion time");

用时:1207ms

改变发生在第 3 行,添加一条语句,将数组变为异构类型。其余代码保持不变。性能差异表现出来了,慢了 22 倍。

旧式数组:读取

var LIMIT = 10000000;
var arr = new Array(LIMIT);
arr.push({a: 22});
for (var i = 0; i < LIMIT; i++) {
arr[i] = i;
}
var p;
console.time("Array read time");
for (var i = 0; i < LIMIT; i++) {
//arr[i] = i;
p = arr[i];
}
console.timeEnd("Array read time");

用时:196ms

Typed Array:读取
var LIMIT = 10000000;
var buffer = new ArrayBuffer(LIMIT * 4);
var arr = new Int32Array(buffer);
console.time("ArrayBuffer insertion time");
for (var i = 0; i < LIMIT; i++) {
arr[i] = i;
}
console.time("ArrayBuffer read time");
for (var i = 0; i < LIMIT; i++) {
var p = arr[i];
}
console.timeEnd("ArrayBuffer read time");

用时:27ms

结论

类型化数组的引入是 JavaScript 发展历程中的一大步。Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,这些是类型化数组视图,使用原生字节序(与本机相同)。我们还可以使用 DataView 创建自定义视图窗口。希望未来会有更多帮助我们轻松操作 ArrayBuffer 的 DataView 库。

JavaScript 数组的演进非常 nice。现在它们速度快、效率高、健壮,在内存分配时也足够智能。

总结

以上所述是小编给大家介绍的JavaScript 数组的进化与性能分析,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
javascript数字数组去重复项的实现代码
Dec 30 Javascript
重构Javascript代码示例(重构前后对比)
Jan 23 Javascript
JavaScript截取、切割字符串的技巧
Jan 07 Javascript
JavaScript的设计模式经典之建造者模式
Feb 24 Javascript
jQuery代码实现对话框右上角菜单带关闭×
May 03 Javascript
jquery层级选择器的实现(匹配后代元素div)
Sep 05 Javascript
深入理解ES6学习笔记之块级作用域绑定
Aug 19 Javascript
highCharts提示框中显示当前时间的方法
Jan 18 Javascript
Layui带搜索的下拉框的使用以及动态数据绑定方法
Sep 28 Javascript
前端vue+elementUI如何实现记住密码功能
Sep 20 Javascript
在Vue中使用Select选择器拼接label的操作
Oct 22 Javascript
Vant+postcss-pxtorem 实现浏览器适配功能
Feb 05 Javascript
JavaScript实现HTML5游戏断线自动重连的方法
Sep 18 #Javascript
Node.JS中快速扫描端口并发现局域网内的Web服务器地址(80)
Sep 18 #Javascript
BetterScroll 在移动端滚动场景的应用
Sep 18 #Javascript
Windows下Node.js安装及环境配置方法
Sep 18 #Javascript
jQuery选择器之子元素选择器详解
Sep 18 #jQuery
JavaScript实现的斑马线表格效果【隔行变色】
Sep 18 #Javascript
js实现可以点击收缩或张开的悬浮窗
Sep 18 #Javascript
You might like
使用PHP下载CSS文件中的图片的代码
2013/09/24 PHP
在Ubuntu 14.04上部署 PHP 环境及 WordPress
2014/09/02 PHP
php实现根据身份证获取精准年龄
2020/02/26 PHP
jQuery ui 1.7更新小结
2009/08/15 Javascript
jQuery ctrl+Enter shift+Enter实现代码
2010/02/07 Javascript
基于jquery的loading效果实现代码
2010/11/05 Javascript
jquery插件开发之实现google+圈子选择功能
2014/03/10 Javascript
JavaScript获取两个数组交集的方法
2015/06/09 Javascript
javascript仿京东导航左侧分类导航下拉菜单效果
2020/11/25 Javascript
浅谈JS正则表达式的RegExp对象和括号的使用
2016/07/28 Javascript
jQuery实现手机版页面翻页效果的简单实例
2016/10/05 Javascript
[Bootstrap-插件使用]Jcrop+fileinput组合实现头像上传功能实例代码
2016/12/20 Javascript
微信小程序 Button 组件详解及简单实例
2017/01/10 Javascript
js eval函数使用,js对象和字符串互转实例
2017/03/06 Javascript
纯js实现图片匀速淡入淡出效果
2017/08/22 Javascript
使用node.js对音视频文件加密的实例代码
2017/08/30 Javascript
vue-router路由模式详解(小结)
2019/08/26 Javascript
vue实现短信验证码登录功能(流程详解)
2019/12/10 Javascript
[02:20]DOTA2中文配音宣传片
2013/05/22 DOTA
[31:33]2014 DOTA2国际邀请赛中国区预选赛 TongFu VS DT 第一场
2014/05/23 DOTA
Python中转换角度为弧度的radians()方法
2015/05/18 Python
python+selenium开发环境搭建图文教程
2017/08/11 Python
pygame实现雷电游戏雏形开发
2018/11/20 Python
Python pandas自定义函数的使用方法示例
2019/11/20 Python
Python面向对象程序设计之类和对象、实例变量、类变量用法分析
2020/03/23 Python
CSS3实现瀑布流布局与无限加载图片相册的实例代码
2016/12/22 HTML / CSS
美国祛痘、抗衰老药妆品牌:Murad
2016/08/27 全球购物
屈臣氏官方旗舰店:亚洲享负盛名的保健及美妆零售商
2019/03/15 全球购物
德国珠宝和手表在线商店:VALMANO
2019/03/24 全球购物
澳大利亚最好的电动自行车:Leon Cycle
2020/12/19 全球购物
反邪教标语
2014/06/23 职场文书
大学生求职信例文
2014/06/29 职场文书
国庆65周年演讲稿:回首往昔,展望未来
2014/09/21 职场文书
音乐课外活动总结
2015/05/09 职场文书
一年级下册数学教学反思
2016/02/16 职场文书
详解Golang如何实现支持随机删除元素的堆
2022/09/23 Python