堆栈内存的分析以及作用域的详解

1,378 阅读5分钟

堆和栈

栈内存和堆内存的概念和作用

js的内存机制主要分成两种,一个是栈内存,一个是堆内存。

栈内存有两个作用

1,给js代码提供自上而下的运行机制,所有的js代码都是在栈内存运行的。 2,因为基础类型比较简单,所以栈内存会直接开辟一个空间把基础类型保存在栈内存里边。 当栈内存被销毁,存储在栈内存中的基本类型的值也会被销毁掉。

堆内存的作用: 存储引用类型的值,是以键值对的方式存储的。 如果堆内存释放了,则引用就彻底消失了。

堆内存的释放?

当堆内存没有被任何的变量或者其他的东西占用,谷歌浏览器(标准的浏览器)会在空闲的时候自动进行内存的回收。把不被占用的内存销毁掉。 谷歌浏览器是每隔一段时间进行内存的释放。 Ie浏览器是计数的方式,当有一个变量引用这个堆内存地址时,则计数为1,如果两个变量引用则计数为2,以此内推,当计数为0,也就是没有变量引用的时候,这个堆内存就会被释放。

内存泄漏

ie浏览器堆的堆内存是以计数的方式实现的,但是有的时候计数会出现混乱,导致计数一直不为0,所以就不会销毁,这种情况就叫做内存的泄露。

代码对内存释放的操作:

let a=引用地址A,如果把a=null,并且这引用地址A只有a引用,那么把a=null时,就是把变量a的引用变成为null。这时引用地址A就失去了引用变量。那么在标准浏览器中空闲的时候会销毁掉这个失去引用变量的引用地址的堆内存,或者ie浏览器已经发现失去了最后一个引用的变量,也会销毁掉这个引用地址的堆内存。

基础类型【变量的提升】:

浏览器在加载页面的时候,会开辟一个全局的作用域,在这个全局的作用域中自上而下的执行程序代码。

在执行的过程当中会遇到var关键字声明的变量

如:var a=1;

这行代码会有3个步骤,分别是:

1,var a;声明一个变量,此时的变量的值是undefined

2,在全局作用域下存储1

3,把这个1(值)和变量a关联起来,我们一般把这个操作叫做定义或者是赋值。

如果遇到值为基础类型的变量的赋值,其实操作的是赋值的变量的值: 如: var b=a; b=2;

分析:var b=a;//其实b和a是没有关系的,其实是b=1;直接等于a的值,这个1同样是存在全局作用域里边。所以目前全局作用域里边村了两个1。

分析:b=2;//一个变量只能有一个值,所以 2存在全局作用域下,并且2和b相关联。而之前的b与1相关联会破裂掉,此时被破裂掉的1因为没有任何变量与它关联,所以会被回收掉。

【引用类型】:

因为引用类型比较复杂,所以引用类型的值会在堆内存中开辟一个空间存储值。

例如,实例-1

        var ary1=[1,2,3];
        var ary2=ary1;
        ary2.push(100);
        console.log(ary1);//[1,2,3,100]
        
        

图解

文字理解

1,首先理解以下堆和栈的概念,栈和堆都是浏览器运行时分配的内存存储区域。 栈主要是存储变量和基础类型的值,堆存储的是复杂的数据,一般都是对象。

全局作用域就是在栈内存中开辟的。

2,栈内存的变量和堆内存中的对象之间的关联是用引用地址进行关联的。具体是,当对象生成时会在堆内存中开辟一个存储的空间,同时生成一个16进制的地址。然后再把这个地址赋值给栈内存的变量。那么这个变量通过引用这个地址到堆内存中寻找这个对象,然后进行操作。

上述实例分析

1,当全局作用域执行 var ary1=[1,2,3]时,会在全局作用域下开辟一个空间存储ary1变量。然后再到堆内存里边生成数组对象进行存储([1,2,3])。再把对象生成的地址(xxxfff)赋值给ary1。

2,var ary2=ary1时,就是ary1的引用值(xxxfff)赋值给ary2.这时的ary2同样可以到堆内存中寻找这个地址所对应的对象。

3,ary2.push(100)操作时,操作的就是地址xxxfff对应的对象。

4,console.log(ary1);ary1的和ary2的值都是xxxfff,所以两个在堆内存中的对象是同一个。

如果对象是函数

如果是函数会分成两个步骤: 第一个步:是当函数定义的时候,会把函数名以变量的形式保存在栈内存中,而函数体相对来说比较复杂,所以是以字符串的方式保存在堆内存中,然后把对内存中的地址赋值给栈内存中的变量。 第二步:函数的执行的时候,会在栈内存中又开辟一个作用域,然后在这作用域中执行函数在堆内存中的函数体。执行的方式和在全局作用域下是一样的。

实例分析

 	var a=1;
        function sum(){
            var b=2;
            console.log(a+b);
        }
        sum();//3
        sum();//3

图解

文字理解: 1,在全局作用域下存储变量和赋值的引用地址,堆内存中存储函数体,并且函数体是以字符串的方式进行存储的。

2,函数在执行的时候,开辟一个新的作用域,如果函数执行多次就会开辟多个作用域,所以这里sum函数开辟了两个作用域。函数作用域和全局作用域执行操作是一样的。

注意:如果函数执行多次则会在栈内存中开辟多个作用域。