3.3 JavaScript的内存模型
JS的本质是一个对象,一个对象可以包含多个属性,对象的属性可以分为直接量和对象两种类型,而对象又分为object对象和function对象两种类型。
直接量和对象两种类型的属性在内存中的保存方式不同。直接量是直接用两块内存分别保存属性名和属性值,而对象需要三块内存,分别保存属性名、属性地址和属性内容,如图3-11和图3-12所示。
图3-11 直接量属性的内存模型图
图3-12 对象属性的内存模型图
对于对象类型的属性来说,属性名只是指向了保存对象的内存地址,而并不是指向实际的对象,从下面的例子就可以看出这一点。
function F(){ this.v = 1; } var f = new F(); var f1 = f; console.log(f1.v); //输出1 f1.v=2; console.log(f.v); //输出2 f = null; console.log(f1.v); //输出2
在上述代码中,首先使用function类型的F创建了object类型的f对象,这时变量f就会指向一个保存新创建的对象的地址。然后,将f赋值给f1变量的操作又将f1指向了创建出来的对象。此时,f和f1都指向了同一个对象,所以f1.v的值就是新创建对象中默认的1、通过f1将v的值修改为2之后,因为是修改了对象中属性v的值,所以使用f.v输出的也是2。最后将f设置为null之后,只是将其对应的地址设置为null,但并不影响对象的内容,通过f1还可以调用。整个流程的结构如图3-13所示。
图3-13 对象的内存处理流程
JS中对象类型的模型类似于Windows系统中的快捷方式。同一个内容,例如一份Excel报表,可以在不同的文件夹下有很多快捷方式,不过无论通过哪个快捷方式打开报表修改内容后,所有快捷方式所打开的报表都会被修改,因为它们所对应的本来就是同一个报表。当然,删除其中一些快捷方式也不会影响报表的内容。Windows中不同的文件夹就相当于JS中的不同对象,所包含的文件相当于JS中的直接量,快捷方式相当于对象属性名。这种类比虽然不够严谨,但用于理解ES的对象结构和内存模型还是比较直观的,读者们可以细心体会一下。
另外,有些读者可能会认为JS是脚本语言,是由解释器执行的,不应该有自己的内存模型,其实并不是这样的。无论编译型语言还是解释型语言,它们的变量、函数、对象等数据都是保存在内存中的,使用时都需要使用变量名在指定地方(例如符号表)找到所对应的具体内容,然后再实际操作。只不过一个是由编译器来做一个是由解释器来做,一个是编译为机器码一个是直接执行相应的操作而已(而且现在JS底层越来越偏向编译执行了)。对于解释型的JS来说,其在内存中保存数据当然也需要有一定的规则,即本节所介绍的内存模型。