1、动态语言和静态语言
静态语言的效率更高
javaScript是一种动态语言,对象的属性在运行时可以被修改。因此,当V8使用一个对象时,比如使用start.x来访问对象start的属性x时,V8并不知道对象中是否存在x属性,也不知道x属性相对于对象的偏移量是多少,也就是说V8不知道对象的具体形状。
相比之下,C++是一种静态语言,对象的结构在编译时就被确定下来。在C++中,访问对象的属性时可以直接根据属性相对于对象地址的偏移值进行访问。例如,在C++中使用start.x时,编译器会将属性x相对于对象start的地址直接写入汇编指令中,当使用对象start的属性x时,CPU可以直接从内存地址中获取属性值,无需进行中间的查找过程。
这就是为什么静态语言的效率更高,因为在编译时就可以确定对象的属性访问方式,而动态语言需要在运行时进行属性查找,导致访问速度较慢且耗时。
V8引入静态的特性-隐藏类
为了提高 JavaScript 对象属性的访问速度,V8 引入了隐藏类的概念。隐藏类是为每个对象创建的一个数据结构,记录了对象的属性和属性的偏移量:
- 对象中所包含的所有的属性;
- 每个属性相对于对象的偏移量。
每个对象都有一个隐藏类,也被称为map,该map属性指向对象的隐藏类。隐藏类包括了对象的属性和它们相对于对象的偏移量。当访问对象的属性时,V8会先查找对象的隐藏类,根据偏移量直接访问属性的值,而不需要进行一系列的查找过程,从而提高了访问属性的效率。
let point = {x:100,y:200}
多个对象共用一个隐藏类
每个对象都有一个 map 属性,该属性值指向该对象的隐藏类。不过如果两个对象的形状是相同的,V8 就会为其复用同一个隐藏类,这样有两个好处:
- 减少隐藏类的创建次数,也间接加速了代码的执行速度;
- 减少了隐藏类的存储空间。
对象的形状是相同的,要满足以下两点:
- 相同的属性名称;(包括顺序)
- 相等的属性个数。
let point = {x:100,y:200};
let point2 = {x:3,y:4};
重新构建隐藏类关于隐藏类
V8 为了实现隐藏类,需要两个假设条件:
-
对象创建好了之后就不会添加新的属性;
-
对象创建好了之后也不会删除属性。
但是,JavaScript 依然是动态语言,在执行过程中,对象的形状是可以被改变的,如果某个对象的形状改变了,隐藏类也会随着改变,这意味着 V8 要为新改变的对象重新构建新的隐藏类,这对于 V8 的执行效率来说,是一笔大的开销。
通俗地理解,给一个对象添加新的属性,删除新的属性,或者改变某个属性的数据类型都会改变这个对象的形状,那么势必也就会触发 V8 为改变形状后的对象重建新的隐藏类。
总结
1,使用字面量初始化对象时,要按照顺序保持属性一致,避免改变对象形状,代码如下所示:
let point = {x:100,y:200};
let point2 = {y:100,x:200};
虽然创建时的对象属性一样,但是它们初始化的顺序不一样,这也会导致形状不同,所以它们会有不同的隐藏类,所以我们要尽量避免这种情况。
2,尽量一次性使用字面量初始化对象的属性,避免重复添加属性。
3,避免使用delete方法,因为它破坏了对象的形状,导致重新生成隐藏类。