javascript基本数据类型赋值和对象引用的浏览器内存情况分析

1,124 阅读4分钟

首先我们定义一个对象c,再定义一个变量d,把c赋值给d。

    let c = {
      name: '我的对象'
    }
    let d = c;
    console.log(c, d);  // {name: "我的对象"} {name: "我的对象"}
    d.name = "我是改变后的对象";
    console.log(c, d);  // {name:"我是改变后的对象"}{name:"我是改变后的对象"}

第一次输出c和d是c对象的原始值,后面我们改变了d对象下的name,第二次输出发现两个都被改变了,那为什么c也会被改变呢?

我来看看浏览器内存内部到底发生了什么!!!

内存信息

打开浏览器内存,可以看到一堆内容,我们过滤掉其他无用的,只选取string类型下面的数据,发现d所修改的内存信息就显示在第二个,点击详情会看到下面红色框里面的内容。

d in system / Context @130419
c in system / Context @130419

后面的@130419就是内存的地址,我们发现d和c的内存第一只是一样的。

基本类型的赋值

上面是对象类型的引用,当引用对象修改之后被引用对象也会被修改。那我们试试看基本类型会不会被修改!

    let a = "我是a";
    let b = a;
    console.log(a, b);  // 我是a 我是a
    b = "我是b,我变了";
    console.log(a, b);  // 我是a 我是b,我变了

输出结果是给b赋值了"我是b,我变了"之后,a并没有被修改。那我们看看内存发生了什么变化? 在内存页面我们再新建一个快照,结果如下:

a的内存信息

b的内存信息

对比两张图

结果是: a的内存地址是 @ 159845 b的内存地址是 @ 152195

通过上面可以发现,基本类型比较的时候是 值 的比较

基本类型的变量是存放在栈内存的

let name = "smallzip"
let age = 22
let isMarried = false

上面变量的存储结构为:

栈内存
name smallzip
age 22
isMarried false

栈内存会记录变量的属性名和对应的值。

引用类型

引用类型指的是那些可能由多个值构成的对象。它的值是保存在内存中的对象。javascript不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象,因此,引用类型的值是按引用访问的。

下面例子所示,可以给引用类型动态的添加或删除属性。

let person = {}
person.name = "smallzip"
person.age = 22
person.isMarried = false
person.hasChild = false

delete person.hasChild

console.loog(person.hasChild) // undefined

let person2 = person
person2.name = "xiaopiao"

console.log(person2.name)  // xiaopiao

引用类型对象赋值在堆内存中的关系

下面例子中是引用类型变量赋值。变量obj1保存了一个对象实例,然后这个值被赋值到了obj2,obj1和obj2指向了同一个对象。为obj1添加name属性,obj2访问这个name属性,因为他们两个引用的是同一个对象,同样指向堆内存中的Object,因此obj1.name赋值会影响obj2.name。下面图展示了保存在变量对象中的变量和保存在堆内存中的对象之间的关系。

let obj1 = new Object();
let obj2 = obj1;
obj1.name = "smallzip";
alert(obj2.name);  // "smallzip"

对象复制的关系

javascript和其他语言不同,它不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间,实际上,是操作对象的引用。

引用类型的对象是放在栈内存中的,而引用类型的原型是放在堆内存中的。

了解栈与堆

还记得文章一开始的a、b、c、d四个变量吗?为方面后面的理解,我们将它们进行简化,代码如下:

let c = {
    name: 'smallzip'
}
let d = c;
console.log(c, d);  // {name: "smallzip} {name:"smallzip"} 
d.name = "xiaopiao";
console.log(c, d);  // {name:"xiaopiao"}{name:"xiaopiao"}

let a = 1;
let b = a;
console.log(a, b);  // 1
b = 2;
console.log(a, b);  // 1 2

四个变量对应的栈和堆图如下:

栈堆图

对象下的对象

日常应用中,往往对象还会存在其他的对象

let person = {
    name:"smallzip",
    family:{
        child:"nike",
        wify:{
            name:"mally",
            mother:{
                // ...
            }
        }
    }
}

上面的案例在堆内存情况如下:

堆内存

那如果我们改变对象的值,会发生什么变化,尝试做下面赋值操作

person.name = "xiaopiao"
person.family.child = "addi"
person.family.wify.name = "july"
person.game = {  // 新增对象
    name:"csgo"
}

堆内存

到此,对于堆内存和栈内存应该已经有所概念。