演化与性子,中数组操作注意点

2019-11-08 13:10栏目:人才招聘
TAG:

给初学者:JavaScript 中数组操作注意点

2017/12/27 · JavaScript · 数组

原文出处: CarterLi   

10种最常见的Web应用程序的性能问题

2015/04/07 · HTML5, JavaScript · 性能

本文由 伯乐在线 - 段昕理 翻译,艾凌风 校稿。未经许可,禁止转载!
英文出处:www.neotys.com。欢迎加入翻译组。

Web应用程序总是不可避免的会发生问题。Neotys公司(法国一家负载测试解决方案提供商)的业务主要是通过网站监控和测试帮助您避免这些问题。但任何地方都可能出问题,有时候你只需要知道去哪找就可以了。因此,我们将您经常碰到的一些性能问题并整理成一个简短的指引。

请务必要记住,解决性能问题的最佳方式就是在其影响用户之前就发现并消除。一个良好的维护计划可以成为你的好帮手。制定停机时间策略,创建冗余和扩展计划。为用户负载在一个月或一年后可能会达到的量级做提前的思考。当然,首先要定期做测试负载并持续监控产品性能。

无论你对网站考虑得如何周全,但是有些问题总是要发生的。下面是一些常见性能问题的原因和解决办法。

深入 JavaScript 数组:进化与性能

2017/09/18 · JavaScript · 数组

原文出处: Paul Shan   译文出处:众成翻译   

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

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

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

不要用 for_in 遍历数组


这是 JavaScript 初学者常见的误区。for_in 用于遍历对象中包括原型链上的所有可枚举的(enumerable)的 key,本来不是为遍历数组而存在。

使用 for_in 遍历数组有三点问题:

遍历顺序不固定

JavaScript 引擎不保证对象的遍历顺序。当把数组作为普通对象遍历时同样不保证遍历出的索引顺序。

会遍历出对象原型链上的值。

如果你改变了数组的原型对象(比如 polyfill)而没有将其设为 enumerable: false,for_in 会把这些东西遍历出来。

运行效率低下。

尽管理论上 JavaScript 使用对象的形式储存数组,JavaScript 引擎还是会对数组这一非常常用的内置对象特别优化。
可以看到使用 for_in 遍历数组要比使用下标遍历数组慢 50 倍以上

PS:你可能是想找 for_of

问题 1: 糟糕的代码

糟糕的代码会使Web应用程序出现诸如算法低效、内存溢出、以及死锁等问题。软件版本过旧,或是集成了历史遗留的系统同样会拖累性能。确保你的团队成员都在使用适合其岗位的工具 – 从自动化分析到最佳编程实践的代码审查工具。

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

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

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

图片 1

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

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

图片 2

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

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

不要用 JSON.parse(JSON.stringify()) 深拷贝数组


有人使用 JSON 中深拷贝对象或数组。这虽然在多数情况是个简单方便的手段,但也可能引发未知 bug,因为:

会使某些特定值转换为 null

NaN, undefined, Infinity 对于 JSON 中不支持的这些值,会在序列化 JSON 时被转换为 null,反序列化回来后自然也就是 null

会丢失值为 undefined 的键值对

JSON 序列化时会忽略值为 undefined 的 key,反序列化回来后自然也就丢失了

会将 Date 对象转换为字符串

JSON 不支持对象类型,对于 JS 中 Date 对象的处理方式为转换为 ISO8601 格式的字符串。然而反序列化并不会把时间格式的字符串转化为 Date 对象

运行效率低下。

作为原生函数,JSON.stringify 和 JSON.parse 自身操作 JSON 字符串的速度是很快的。然而为了深拷贝数组把对象序列化成 JSON 再反序列化回来完全没有必要。

我花了一些时间写了一个简单的深拷贝数组或对象的函数,测试发现运行速度差不多是使用 JSON 中转的 6 倍左右,顺便还支持了 TypedArray、RegExp 的对象的复制

https://jsperf.com/deep-clone…

问题 2:未经优化的数据库

优化好的数据库可以达到很好的安全级别和处理性能,反之,没有优化的数据库可能会拖垮生产环境下的应用程序。索引的缺失会减慢SQL的查询性能,从而使整个网站变慢。一定要用脚本和文件分析检查任何低效的查询。

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;

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

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

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

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

不要用 arr.find 代替 arr.some


Array.prototype.find 是 ES2015 中新增的数组查找函数,与 Array.prototype.some 有相似之处,但不能替代后者。

Array.prototype.find 返回第一个符合条件的值,直接拿这个值做 if 判断是否存在,如果这个符合条件的值恰好是 0 怎么办?

arr.find 是找到数组中的值后对其进一步处理,一般用于对象数组的情况;arr.some 才是检查存在性;两者不可混用。

问题 3:失控的数据增长

数据系统一般会随时间的推移变慢。制定一项计划来管理和监控数据,因为维持数据的增长对高性能的Web应用不可或缺。首先,找出业务中导致数据增长的主因。然后,研究并制定合适的存储解决方案。留意所有数据库、缓存、以及更复杂存储方案的选项。

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

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

不要用 arr.map 代替 arr.forEach


也是一个 JavaScript 初学者常常犯的错误,他们往往并没有分清 Array.prototype.map 和 Array.prototype.forEach 的实际含义。

map 中文叫做 映射,它通过将某个序列依次执行某个函数导出另一个新的序列。这个函数通常是不含副作用的,更不会修改原始的数组(所谓纯函数)。

forEach 就没有那么多说法,它就是简单的把数组中所有项都用某个函数处理一遍。由于 forEach 没有返回值(返回 undefined),所以它的回调函数通常是包含副作用的,否则这个 forEach 写了毫无意义。

确实 map 比 forEach 更加强大,但是 map 会创建一个新的数组,占用内存。如果你不用 map 的返回值,那你就应当使用 forEach

问题 4:流量峰值

我们通常认为流量的增长是件好事。但是当做完市场推广或是经历了疯狂传播的热门视频后,应用程序如果没有做好相应的准备,任何人都知道流量峰值会造成什么结果。提前准备是关键,同时搭建一个通过模拟用户做监测的预警系统例如 NeoSense。这样一来,你就会提前发现流量增长影响到了业务,从而避免了用户的糟糕体验。

旧式数组:插入

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");

1
2
3
4
5
6
7
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

版权声明:本文由ag真人发布于人才招聘,转载请注明出处:演化与性子,中数组操作注意点