前言
在js的对象中,每个key映射的属性值都带有一套*property attributes*.
如上图所示,get,set,writable等属性,虽然在我们平时的编码中,很多时候可能不需要使用,但是js任然要对对象的每一个属性都维护一套property attributes
.这样无形之中,大大增加了js的内存消耗,那么在js中,是用什么方法对其进行优化的呢?
Shapes
什么是shape
js中,将一个类对应的属性数据结构称为Shape
如上图所示,有包含x,y的对象Object, js将其分作两部分来存储。JSObject类似于一个只包含x,y的值的数组,和一个包含了x,yproperty attributes
的shape
。JSObject中的value按顺序对应shape
中的property attributes
的shape
。
这样在面对大量结构相同的对象时,只需要存储一套Shape
,在单独维护JSObject中的value即可,大大减小了性能开销
例如上图所示,a,b两个对象便可以复用一套shape
。
shape的深入优化
js
const point = {};
point.x = 4;
point.y = 5;
point.z = 6;
在上面的代码中,js引擎并不会对一个shape
进行多次修改,而是生成3个shape
,以链状的形式排列。
这种优化方式,例如在处理包含非常多属性的对象时,对其增加或者删除属性,就不需要重新维护一个其的赋值shape
,有效减小内存开销。
如果对象中的属性太多,shape
的链条太长,js引擎还会维护一个shapeTable
来索引Shape
,减小了链式查找带来的性能开销。
注意: 添加特性的顺序会影响shape
结构。例如,{x:4,y:5}会产生与{y:5,x:4}不同的shape
。
而对于Array来说,则不需要特定存住其shape
,因为Array所有的属性的property attributes
一般都是默认的。也可以用defineProperty修改,但是及其不推荐。
shape的实际场景分析
现在有如下两个生成对象的方法
js
<button onclick="samllFunc()">内存小方式</button>
<button onclick="bigFunc()">内存大方式</button>
<script>
let list = [];
function samllFunc() {
function addKey(item) {
for(let i = 0; i < 10000; i++) {
item[i] = 1;
}
return item
}
let res = [];
for(let i = 0; i < 10000; i++) {
res.push(addKey({}))
}
list.push(res)
console.log(res.length)
}
function bigFunc() {
function addKey(item, key) {
for(let i = 0; i < 10000; i++) {
item[i + key] = 1;
}
return item
}
let res = [];
for(let i = 0; i < 10000; i++) {
res.push(addKey({}, i))
}
list.push(res)
console.log(res.length)
}
</script>
都是生成10000个10000个属性的对象,smallFunc
中的对象都含有相同的shape
,bigFunc
中的对象shape
各不相同。
相同的shape
快照:
不同的shape
快照:
对比可以看出,两者的差距是非常大的。 、
总结
基于对shape
的理解,我们确定了一些有助于提高性能的实用JavaScript编码技巧:
- 总是以相同的方式初始化对象,这样它们就有相同的形状可以复用。
- 不要扰乱数组元素的属性,这样可以有效地存储和操作它们。