一、认识堆结构

先从一条面试题开始:说说var const let的区别,相信这个问题大家都能说很多;下一个问题:const定义的值能修改吗?这个很多该说不能。
但是呢:正确答案是部分能,部分不能。
解析:const定义的基本变量类型是不能修改的,但是定义的对象是可以通过修改对象属性等方法进行修改。
因此当我们定义一个const对象的时候,我们说的常量其实是指针,就是const对象对应的堆内存指向是不变的,但是堆内存中的数据本身的大小或者属性是可变的。而对于const定义的基础变量而言,这个值就相当于const对象的指针,是不可变。

  1. const obj = {
  2. name: 'zhangsan',
  3. age: 18
  4. }
  5. const conNum = 1
  6. // 如果直接修改conNum的值就会报错
  7. conNum = 2 // 报错
  8. // 但是如果修改obj里面的值,就可以修改
  9. obj.name = 'lisi'
  10. console.log(obj.name) // lisi
  • 堆是一种经过排序的树形数据结构,每个结点都有一个值。(是一种树状结构)
  • 堆的存取是随意,这就如同我们在图书馆的书架上取书。
  • 好比在JSON格式的数据中,我们存储的key-value是可以无序的,只要知道key,就能取出这个key对应的value。

    二、栈内存和堆内存的示意图

    image.png
    栈内存中的变量一般都是已知大小或者是有范围上限的,算是一种简单的存储。这也是会出现栈溢出的原因之一。
    堆内存中存储的对象类型数据大小方面一般都是未知的,也就是可以存比较大的数据。

    三、内存分配和垃圾回收

  • 一般来说栈内存线性有序存储,容量小,系统分配效率高。

  • 而堆内存首先要在堆内存新分配存储区域,之后又要把指针存储到栈内存中,效率相对就要低一些了。
  • 垃圾回收方面,栈内存变量基本上用完就回收了,而推内存中的变量因为存在很多不确定的引用,只有当所有调用的变量全部销毁之后才能回收。

    四、JavaScript中的基本类型和引用类型与堆栈的关系

    1、基本类型

    Undefined Null Boolean Number String五种基本数据类型在内存中分别占有固定大小的空间,他们的值保存在栈空间(内存)中,按值访问,因此可以操作保存在变量中的实际的值

    2、引用类型

    引用类型的值保存在堆内存中,值大小不固定,栈内存中存放地址指向堆内存中的对象按引用访问。
    栈内存中存放的只是该对象的访问地址,在堆内存中为这个值分配空间。由于这种值的大小不固定,因此不能把它们保存到栈内存中。但内存地址大小的固定的,因此可以将内存地址保存在栈内存中。 这样,当查询引用类型的变量时, 先从栈中读取内存地址, 然后再通过地址找到堆中的值。对于这种,我们把它叫做按引用访问
    image.png

    五、复制变量(深浅拷贝)

    1、基本类型的拷贝

    ```javascript var a = 20; var b = a; b = 30;

// 这时a的值是多少? 20

  1. **在变量对象中的数据发生复制行为时,系统会自动为新的变量分配一个新值,最后这些变量都是相互独立互不影响的**。var b = a执行之后,ab虽然值都等于20,但是他们其实已经是相互独立互不影响的值了。由于基本类型是按值访问的,所以我们修改了b的值以后,a的值并不会发生变化。<br />![image.png](https://cdn.nlark.com/yuque/0/2022/jpeg/435549/1654925869003-1c67c01b-45c7-45d4-acb6-316c70cb45d0.jpeg#clientId=u68484c63-7bc0-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u4e091286&margin=%5Bobject%20Object%5D&name=image.png&originHeight=528&originWidth=1254&originalType=url&ratio=1&rotation=0&showTitle=false&size=142022&status=done&style=none&taskId=u05193e62-46b4-42a7-8615-d5e8214724b&title=)
  2. <a name="DjRAn"></a>
  3. ## 2、引用类型的拷贝
  4. 基本类型拷贝的时候只是在内存中又开辟了新的空间,和它被拷贝的对象属于 互不想干的东西,因此深浅拷贝是相对于引用类型的。
  5. ```javascript
  6. var m = { a: 10, b: 20 }
  7. var n = m;
  8. n.a = 15;
  9. // 这时m.a的值是多少 15
  • 引用类型的复制,同样为新的变量n分配一个新的值,保存在栈内存中,不同的是,这个值仅仅是引用类型的一个地址指针;
  • 他们两个指向同一个值,也就是地址指针相同,在堆内存中访问到的具体对象实际上是同一个;
  • 因此改变n.a时,m.a也发生了变化,这就是引用类型的特性。

image.png

  • 浅拷贝:当我们改变子对象(复制出来的新对象)时,父对象(被拷贝的对象)也会跟着改变的拷贝。也就是说,子对象和父对象在浅拷贝的时候他们指向同一个内存的对象。
  • 深拷贝:就是把父对象拷贝到子对象上,而且两者的内存和以后的操作都互不影响的拷贝!

    3、深拷贝和浅拷贝的区别

    4、深拷贝的方法

    5、深拷贝方法中JSON.Stringfiy()有什么坏处

    六、栈内存和堆内存的总结

    | 栈内存 | 堆内存 | | —- | —- | | 存储基础数据类型 | 存储引用数据类型 | | 按值访问 | 按引用访问 | | 存储的值大小固定或者是有范围上限 | 存储的值大小不固定,可动态调整 | | 由系统自动分配内存空间 | 由代码进行指定分配 | | 空间小,运行效率高 | 空间大,运行效率相对较低 | | 先进后出,后进先出 | 无序存储,可根据引用直接获取 |