JavaScript数组进化与性能分析

正式开始前需要声明,本文并不是要讲解 javascript 数组基础知识,也不会涉及语法和使用案例。本文讲得更多的是内存、优化、语法差异、性能、近来的演进。本文主要和大家介绍javascript 数组的进化与性能分析,本文讲得更多的是内存、优化、语法差异、性能、近来的演进。需要的朋友可以参考下,希望能帮助到大家。

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

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

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

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

立即学习“Java免费学习笔记(深入)”;

数组是一串连续的内存位置,用来保存某些值。注意重点,“连续”(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;

登录后复制

高性能、高效率的类型化数组在 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数组方法

以上就是JavaScript数组进化与性能分析的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2786672.html

(0)
上一篇 2025年3月8日 17:51:24
下一篇 2025年3月8日 17:51:37

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • JavaScript实现HTML5游戏断线自动重连

    断线重连的需求一断线重连原理二游戏内自动重连不刷新三刷新游戏自动重连重连数据locationreplace重置url重连四实际项目中处理重连机制最后的总结断线重连的需求,尤其是手机上,会因为网络的不稳定或者其他原因,导致用户的socket链…

    编程技术 2025年3月8日
    200
  • JavaScript实现斑马线表格示例分享

    虽然现在有很多框架可以轻松的实现斑马线效果,而且兼容性也很不错,比如bootstrap,但是不可否认的是使用javascript实现的是兼容性最强的(浏览器不支持或禁止javascript脚本除外),所以今天使用原生js实现了一个斑马线效果…

    编程技术 2025年3月8日
    200
  • js点击收缩或张开的悬浮窗实例分享

    本文主要和大家介绍了js实现可以点击收缩或张开的悬浮窗效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能帮助到大家。 说明:点击”+“按钮,悬浮窗收缩/展开 思路 1、在html中定义一个p块,定一个id;一个按钮,点击时用。 …

    编程技术 2025年3月8日
    200
  • React 16.3新特性分析

    context api总是很让人迷惑。这个api是官方的,但是官方又不希望开发者们使用这个api,说是这个api会在以后发生改变。现在就是那个改变的时刻。新的api已经被merge了。而且它看起来更加的“用户友好”了。尤其是你不得不使用re…

    编程技术 2025年3月8日
    200
  • SVG和Vanilla JS框架创建一个“星形变心形”代码分享

    本文我们主要和大家分享用svg和vanilla js框架创建一个“星形变心形”的动画效果代码,希望能帮助到大家。 思路 它们都是由五个三次贝塞尔曲线构成。下边的互动演示展示了每条曲线以及这些曲线相连接的点。点击任意曲线或连接点可以看到两个图…

    编程技术 2025年3月8日
    200
  • 改变JavaScript对象的rest和spread属性方法

    在JavaScript中合并多个对象是一个很常见的事情。但在JavaScript中,到目前为止并没有一种很方便的语法来进行合并。本文主要和大家分享三个点如何改变JavaScript对象的rest和spread属性。 在ES5中,通过使用Lo…

    2025年3月8日 编程技术
    200
  • Vue.js响应式原理详解

    本人是Java背景,许多年前刚接触JavaScript时有点怪怪的,因为它没有 getters 和 setters。随着时间的推移,我开始喜欢上这个缺失的特性,因为相比Java大量的 getter 和 setter,它让代码更简洁。例如,我…

    2025年3月8日
    200
  • javascript中的隐式调用详解

    所谓的隐式调用简单来说就是自动调用一些方法,而这些方法像钩子一样可以在外部修改,从而改变既定行为。 下面我会列举一些最近看到的隐式调用,例子都是点到即止,欢迎补充 数据类型转换 toSting 和 valueOf var obj = { a…

    编程技术 2025年3月8日
    200
  • js之trim函数实现删除两端空格

    本文主要和大家介绍了js自定义trim函数实现删除两端空格功能,结合实例形式分析了javascript基于正则替换实现类似trim函数删除字符串两端空格的相关操作技巧,希望能帮助到大家。 兼容IE低版本浏览器,以及其他一些低版本脚本的浏览器…

    编程技术 2025年3月8日
    200
  • 详解JavaScript的运行原理

    本文主要给大家从理论详细分析了javascript运行原理以及知识点分享,对此有兴趣的学习下吧。 JavaScript是一种基于对象的动态、弱类型脚本语言(以下简称JS),是一种解释型语言,和其他的编程语言不同,如java/C++等编译型语…

    编程技术 2025年3月8日
    200

发表回复

登录后才能评论