1. JS堆栈原理

芮承运
2023-12-01

现在的随着前端开发者如雨后春笋般的诞生,越来越多的项目落地,JavaScript是我们前端开发中必不可少的一环。今天让我们来了解一下JavaScript中的堆栈原理

明白的堆栈原理之后也可以让我们的代码更加的贴近编译器,更加的明白系统对内存的分配, 帮助我们能够更好的管理内存

堆&栈

堆和栈是我们常用的数据结构,还有图,树,链表等,感兴趣的可以读一下数据结构-百度百科
两者都是存放临时数据的地方。


  1. 栈是先进后出的,就像一个桶,后进去的先出来,它下面本来有的东西要等其他出来之后才能出来。

  2. 堆是在程序运行时,而不是在程序编译时,申请某个大小的内存空间。即动态分配内存,对其访问和对一般内存的访问没有区别。对于堆,我们可以随心所欲的进行增加变量和删除变量,不用遵循次序。

栈区(stack) 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。
堆区(heap) 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。
堆(数据结构):堆可以被看成是一棵树,如:堆排序; 栈(数据结构):一种先进后出的数据结构。相反的队列就是一种先进先出的数据结构

数据访问

  1. 基本数据类型:基本数据类型值指保存在栈内存中的简单数据段。访问方式是按值访问。
    undefined,null,布尔值(Boolean),字符串(String),数值(Number)
var a = 1
var b = a
console.log(a) // 1
console.log(b) // 1
a = 3
console.log(a) // 3
console.log(b) // 1

上面代码实际上是为a在栈中创建了一个值,第二句把一个变量向一个变量复制时,会在栈中创建一个新值,然后把值复制到为新变量分配的位置上。

变量
a1
b1

重新为a赋值后

变量
a3
b1
  1. 引用数据类型:引用数据类型值指保存在堆内存中的对象。也就是,变量中保存的实际上的只是一个指针,这个指针指向内存中的另一个位置,该位置保存着对象。访问方式是按引用访问。
    统称为Object类型,细分的话,有:Object类型,Array类型,Date类型,Function类型等。
var obj = {}
obj.a = 1
var copyObj = obj
console.log(obj) // {a: 1}
obj.a = 2
console.log(obj) // {a: 2}
console.log(copyObj) // {a: 2}

上面的第一句在栈中为obj创建了一个值,并且在堆中为obj分配了一个地址,第三句我们在栈中创建了一个copyObj,此时他的地址也指向了堆中的这个地址,改变obj或者copyObj的属性两者都会发生改变,他们在堆中公用一个内存地址

此时我们是否可以理解堆栈溢出

堆栈溢出

堆栈溢出就是不顾堆栈中分配的局部数据块大小,向该数据块写入了过多的数据,导致数据越界,结果覆盖了别的数据。 可以理解为 在长字符串中嵌入一段代码,并将过程的返回地址覆盖为这段代码的地址,这样当过程返回时,程序就转而开始执行这段自编的代码了。

就是你的变量定义过多了,占用内存过多了,浏览器或者操作系统已经不能容忍你了

  1. 简单的谈下浅拷贝

在定义一个对象或数组时,变量存放的往往只是一个地址。当我们使用对象拷贝时,如果属性是对象或数组时,这时候我们传递的也只是一个地址。因此子对象在访问该属性时,会根据地址回溯到父对象指向的堆内存中,即父子对象发生了关联,两者的属性值会指向同一内存空间。

var a = {
   key1:"11111"
}
function Copy(p) {
   var c = {};
   for (var i in p) { 
     c[i] = p[i];
   }
   return c;
}
a.key2 = ['小辉','小辉'];
var b = Copy(a);
b.key3 = '33333';
alert(b.key1); // 1111111
alert(b.key3); // 33333
alert(a.key3); // undefined

a对象中key1属性是字符串,key2属性是数组。a拷贝到b,12属性均顺利拷贝。给b对象新增一个字符串类型的属性key3时,b能正常修改,而a中无定义。说明子对象的key3(基本类型)并没有关联到父对象中,所以undefined。

常见的实现方式

  • 上面的方法
  • Object.assign({}, 要拷贝的对象)
  • jqueryextend方法
  • lodash中的方法
  • 其他
  1. 简单的 谈下深拷贝

深拷贝会为原对象和拷贝对象从新在栈和堆中分配一个地址,两者之间的操作互不影响
常用的实现方式

  • 递归上面的浅拷贝中的方法
  • jquery中的extend方法
  • lodash_.deep(obj)
  • JSON.parse(JSON.stringify(obj))
  • 其他

深浅拷贝 的主要区别就是:复制的是引用(地址)还是复制的是实例。

参考

 类似资料: