牛叔叔 的笔记

好好学习

2024-04-30 12:03

今天来聊聊js中的堆栈是怎么回事

牛叔叔

WEB前端

(28)

(0)

收藏

1.含义及对比

堆和栈都是运行时内存中分配的一个数据区,因此也被称为堆区和栈区;

二者存储的数据类型和处理速度不同;

堆(heap)用于复杂数据类型(引用类型)分配空间,例如数组对象、object对象;它是运行时动态分配内存的,因此存取速度较慢。

栈(stack)中主要存放一些基本类型的变量和对象的引用,(包含池,池存放常量),其优势是存取速度比堆要快,并且栈内的数据可以共享,但缺点是存在栈中的数据大小与生存期必须是确定的,缺乏灵活性,

先进后出,后进先出原则,所以 push 优于 unshift

1.1 扩展: 队列(先进先出(FIFO)的数据结构,这是事件循环(Event Loop)的基础结构)

1.2 经典面试题

var a = 20;
var b = a; // b 为栈类型,相当于新开一块空间然后赋值为 20,不影响原来的a
b = 30;
console.log(a)
console.log(b)
//20 30
var a = { name: '前端开发' }
var b = a;
b.name = '进阶';
console.log(a)
console.log(b)
// { name: '进阶' }  name: '进阶' }
var a = { name: '前端开发' }
var b = a;
a = null;    //释放引用
console.log(a)
console.log(b)
// null { name: '前端开发' }

 

var a = {n: 1};
var b = a;
a.x = a = {n: 2};   //   等价于  b.x = a = {n: 2};            
//先是预编译ab同时指向的对象增加x属性,该对象也就是后来都没变的b指向的对象,这里  a= {n: 2};改变了a的指向。

b // 1    
a  // undefined
console.log(a.x);
console.log(b.x);// undefined {n:2}

  

1.3  闭包中的变量并不保存中栈内存中,而是保存在堆内存中,这也就解释了函数之后之后为什么闭包还能引用到函数内的变量。

function A() { let a = 1 function B() { console.log(a) } return B }

1.4 垃圾回收算法  ( 常见内存泄漏:全局变量、定时器、闭包、dom引用 )

  • 引用计数(现代浏览器不再使用)     循环引用 问题  



  • function cycle() { 
    var o1 = {}; 
    var o2 = {};
    o1.a = o2;
    o2.a = o1; 
    return "cycle reference!" }
    cycle();
  • 标记清除(常用)  从根部(在JS中就是全局对象)出发定时扫描内存中的对象,凡是能从根部到达的对象,保留。那些从根部出发无法触及到的对象被标记为不再使用,稍后进行回收。

 

2.例子


栈
var a=3;
var b=3;

先处理 var a=3;,首先会在栈中创建一个变量为a引用,然后查找栈中是否有3这个值,如果没有找到,就将3存放进来,然后将a指向3。
接着处理 var b=3;,在创建为b的引用变量后,查找栈中是否有3这个值,因为此时栈中已经存在了3,便将b直接指向3。这样,就出现了a与b同时指向3的情况。
此时,如果再令a=4,那么JavaScript解释引擎会重新搜查栈中是否有4这个值,如果已经有了,则直接将a指向这个地址。没有的话直接压入并指向它。
堆
var fruit_1="apple";
var fruit_2="orange";
var fruit_3="banana";
var oArray=[fruit_1,fruit_2,fruit_3];  (此时相当于新建一个包含三个值的数组,与 fruit等不再相关)
var newArray=oArray;
当创建数组时,就会在堆内存创建一个数组对象,并且在栈内存中创建一个对数组的引用
变量fruit_1、fruit_2、fruit_3为基本数据类型,它们的值直接存放在栈中
newArray、oArray为复合数据类型(引用类型),他们的引用变量存放在栈中,     指向于存放在堆中的实际对象。
比较
var str=new String('abc');
var str='abc';
第一种是用new关键字来新建String对象,对象会存放在堆中,每调用一次就会创建一个新的对象,而不管其字符串值是否相等及是否有必要创建新对象,从而加重了程序的负担。并且堆的访问速度慢。
第二种是在栈中,栈中存放值‘abc’和对值的引用;这种写法在内存中只存在一个值,有利于节省内存空间。同时它可以在一定程度上提高程序的运行速度,因为存储在栈中,其值可以共享,并且由于栈访问更快;
var str1='abc';
var str2='abc';
alert(str1==str2); // true
alert(str1===str2); // true
var str1=new String('abc');
var str2=new String('abc');
alert(str1==str2); // false
alert(str1===str2); // false

3.细节

JavaScript堆不需要程序代码来显示地释放,因为堆是由自动的垃圾回收来负责的,每种浏览器中的JavaScript解释引擎有不同的自动回收方式

一个最基本的原则是:如果栈中不存在对堆中某个对象的引用,那么就认为该对象已经不再需要,在垃圾回收时就会清除该对象占用的内存空间。

因此,在不需要时应该将对对象的引用释放掉(newArray=null;),以利于垃圾回收,这样就可以提高程序的性能。


0条评论

点击登录参与评论