3分钟弄清楚javascript的堆栈原理

除了保存的方式不同之外,在从一个变量向另一个变量复制基本类型值和引用类型值时,也存在不

同。如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制

到为新变量分配的位置上。来看一个例子:

var num1 = 5;

var num2 = num1;

在此,num1 中保存的值是 5。当使用 num1 的值来初始化 num2 时,num2 中也保存了值 5。但 num2

中的 5 与 num1 中的 5 是完全独立的,该值只是 num1 中 5 的一个副本。此后,这两个变量可以参与任

复制前的变量对象

复制后的变量对象

引用数据类型:引用数据类型值指保存在堆内存中的对象。也就是,变量中保存的实际上的只是一个指针,这个指针指向内存中的另一个位置,该位置保存着对象。访问方式是按引用访问。

var a = new Object ();

当操作时,需要先从栈中读取内存地址,然后再延指针找到保存在堆内存中的值再操作。

a. name = ‘renlei’ ;

引用类型变量的复制:当从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到 为新变量分配的空间中。不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一 个对象。复制操作结束后,两个变量实际上将引用同一个对象。因此,改变其中一个变量,就会影响另 一个变量;

var b = a;

b.name=“张三”;

b.sex = ‘boy’ ;

深浅拷贝

将一个变量的值赋值给另一个变量,相当于在栈内存中创建了一个新的内存空间,然后从栈中复制值,存储到这个新空间中。对于基本类型,栈中存储的就是它自身的值,所以新内存空间存储的也是一个值。直接改变新变量的值,不会影响到旧变量的值,因为他们值存储的内存空间不同。

// 基本类型复制变量

var a = 10;

var b = a;

b = 20;

a // 10

b // 20

而对于引用类型来说,同样是复制栈中存储的值。但是栈存储的只是其引用地址,其具体的值存储在堆中。变量复制仅复制栈中存储的值,不会复制堆中存储的值,所以新变量在栈中的值是一个地址指针。

// 引用类型复制变量

var a = { age: 27 };

var b = a;

b.age = 29;

a.age == b.age; // 29

可见,变量复制赋值,都属于栈存储拷贝,因此深浅拷贝可以这样区分:

  • “浅拷贝:栈存储拷贝”

  • “深拷贝:栈堆存储拷贝”

深拷贝会同时开辟新的栈内存,堆内存空间。

// 利用JSON对象方法实现深拷贝

var a = { age: 27 };

var b = JSON.parse(JSON.stringify(a));

b.age = 29;

a.age // 27

b.age // 29

函数传参数是按值传递?按引用传递?

var person = {

age: 27

};

function foo (person) {

person.age = 29;

}

foo(person);

person.age // 29;

函数调用时,会对参数赋值。而参数传递过程其实同样是变量复制的过程,所以它是按值传递。var person = person,因为传递参数是对象时,变量复制仅复制的栈存储(浅拷贝),所以修改对象属性会造成外部变量对象的修改。

至此,当我们理清栈、堆数据结构,以及JS中数据类型存取方式。深浅拷贝问题也就通顺了。

内存空间


内存空间管理

JavaScript执行过程中内存分配:

  1. 为变量对象分配需要的内存

  2. 在分配到的内存中进行读/写操作

  3. 不再使用时将其销毁,释放内存

内存管理不善,会出现内存泄露,造成浏览器内存占用过多,页面卡顿等问题。

垃圾回收机制

JavaScript中有自动垃圾回收机制,会通过标记清除的算法识别哪些变量对象不再使用,对其进行销毁。开发者也可在代码中手动设置变量值为null(a = null)进行标记清除,让其失去引用,以便下一次垃圾回收时进行有效回收。

局部环境中,函数执行完成后,函数局部环境声明的变量不再需要时,就会被垃圾回收销毁(理想的情况下,闭包会阻止这一过程)。

全局环境只有页面退出时才会出栈,解除变量引用。所以开发者应尽量避免在全局环境中创建全局变量,如需使用,也要在不需要时手动标记清除,将其内存释放掉。

为什么会有栈内存和堆内存之分?

通常与垃圾回收机制有关。为了使程序运行时占用的内存最小。

当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将自然销毁了。因此,所有在方法中定义的变量都是放在栈内存中的;

当我们在程序中创建一个对象时,这个对象将被保存到运行时数据区中,以便反复利用(因为对象的创建成本通常较大),这个运行时数据区就是堆内存。堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(方法的参数传递时很常见),则这个对象依然不会被销毁,只有当一个对象没有任何引用变量引用它时,系统的垃圾回收机制才会在核实的时候回收它。

类似文章: 
js中的栈、堆、队列、内存空间
前端进击的巨人(一):执行上下文与执行栈,变量对象
浅析JS中的堆内存与栈内存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值