在我前面的文章中,有写到 V8 的内存管理机制,其中 就有 标记整理 算法
但是如果有同学 对js 语言底层的实现 深入了解的话,肯定会疑惑,为什么需要整理内存,获得一个大的连续空间呢?
- js 是一个 万物皆对象的语言,functon,number,string 等等都是对象,就连 Array 都是对象
- 而理论上来说,一个对象是 hash 表结构存储数据的
- 而hash 表结构存储数据的话,就意味着 他其实不需要 多么大的连续空间,就能够存储很多很多的数据
所以说,这里需要更加深入地去了解 V8 内部到底是怎么处理 一个 对象数据的
1、一个普通的对象内部存储方式
v8引擎 为了增加读取数据的性能考虑,其实做了很多很多的优化
- 在一个普通的对象之中,存放属性分为 两个区域,一个叫做快区域 名字叫做 elements ,一个叫做慢区域 叫做 properties
- 快区域是使用 连续内存进行存储的,而慢区域则是 使用hash 表结构存储的
- 当读取一个属性的时候,v8 先是在 快区域中寻找,没找到才会去 慢区域寻找
- 如下面的内容所示,只要是 数字类型的,就被放在了 elements 里面,而 其他类型的,被放在了 properties 里面
- 而数组类型的 ,自然在大多数情况下都被放在了 elements 里面,也就是说,在 js 里,其实数组 还真是被放在连续内存里面的
function FS(fast, slow) {
this.fast2slow = {}
for (let i = 0; i < fast; i ++) {
this[i] = 'fast'
}
for (let i = 0; i < slow; i++) {
this[`slow${i}`] = slow
}
}
var fs = new FS(10, 10)
在某些文章中,有写到 当 数字大到一定程度的时候,数组 的存储结构也会变成一个 hash 表
function FS1(fast, slow) {
this.fast2slow = new Array(10)
}
function FS2(fast, slow) {
this.fast2slow = new Array(100000000)
}
var fs1 = new FS1()
var fs2 = new FS2()
- 在 fs1 中,可以很明显的看到 elements 里面已经没有再存储数据了
- 在 fs2 中,则还有 数据在存储
2、隐藏类
- 众所周知的是,js 是一门 动态语言类型,不像 java 语言那样,有着静态类型
- 但是 在这方面 就会出现一个问题,那就是读取数据的时候不如静态语言快速
- 静态语言中的 类 会对 某一个属性 进行规定,然后快速地查找到这个值,而不是像 动态语言一样,需要遍历一边所有的属性,因为 V8 引擎不知道 开发者内部到底有没有这个类
- 那么为了这个,V8 引擎为此又做出了什么优化呢?
- 在一个对象创建了之后,V8 引擎会为其分配一个 隐藏类
- 就像是 静态语言的类一样,可以帮助 V8引擎 快速地定位到一个元素的所在
- 在类似的数据结构中,V8 引擎会为其分配相同的类,也就是 相同的属性名称,相同的属性数量
- 有了隐藏类之后,那么当 V8 访问某个对象中的某个属性时,就会先去隐藏类中查找该属性相对于它的对象的偏移量,有了偏移量和属性类型,V8 就可以直接去内存中取出对于的属性值,而不需要经历一系列的查找过程,那么这就大大提升了 V8 查找对象的效率
3、优化
所以了解了这个之后,又有什么用呢?
- 使用 typeScript 确实在某种意义上能够帮助我们加快 js 的运行速度
- 不要随便地使用 delete 或者 增加一个属性,因为这样会导致 隐藏类失效而 重新 构建一个新的隐藏类
- 使用字面量初始化对象时,要保证属性的顺序是一致的,
- 例如 var a = {x: 1, y: 2}; b = {y: 1, x : 2} 这样会导致 创建两个 隐藏类
学习自 李兵 老师的 《图解 Google V8》
4、其他的优化方案
- 慎用全局变量,因为会顺着作用域链查找变量,查找的层级越高,速度就越慢
- 缓存全局变量,例如: 在 一个函数内部,如果需要调用 document.createElement 等函数的时候,先将var doc = document 缓存起来,这样 doc 就不是全局变量了,而是 在 同一个 作用域中的值
- 通过原型链增加方法
- 创建 多个 dom 节点的时候,可以使用 document.createElementFrment 或者 先使用字符串拼接,再一次性 innerhtml
- 使用 字面量的 方法创建一个 数组或者对象,而不是使用 new Object 或者 new Array
- 在数组的循环中 forEach 的 运行速度 > for > for in