关于JS堆栈与拷贝

http://www.cnblogs.com/chengguanhui/p/4737413.html

关于JS堆栈与拷贝

1、栈(stack)堆(heap)

  stack为自动分配的内存空间,它由系统自动释放;而heap则是动态分配的内存,大小不定也不会自动释放。       

2、基本类型和引用类型

  基本类型:存放在栈内存中的简单数据段,数据大小确定,内存空间大小可以分配。

  5种基本数据类型有Undefined、Null、Boolean、Number 和 String,它们是直接按值存放的,所以可以直接访问。

  引用类型:存放在堆内存中的对象,变量实际保存的是一个指针,这个指针指向另一个位置。每个空间大小不一样,要根据情况开进行特定的分配。

  当我们需要访问引用类型(如对象,数组,函数等)的值时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。

3、传值与传址

  前面之所以要说明什么是内存中的堆、栈以及变量类型,实际上是为下文服务的,就是为了更好的理解什么是“浅拷贝”和“深拷贝”。

  基本类型与引用类型最大的区别实际就是传值与传址的区别。测试用例:

 1     var a = [1,2,3,4,5];
 2     var b = a;
 3     var c = a[0];
 4     alert(b);//1,2,3,4,5
 5     alert(c);//1
 6     //改变数值
 7     b[4] = 6;
 8     c = 7;
 9     alert(a[4]);//6
10     alert(a[0]);//1

  从上面我们可以得知,当我改变b中的数据时,a中数据也发生了变化;但是当我改变c的数据值时,a却没有发生改变。

  这就是传值与传址的区别。因为a是数组,属于引用类型,所以它赋予给b的时候传的是栈中的地址(相当于新建了一个不同名“指针”),而不是堆内存中的对象。而c仅仅是从a堆内存中获取的一个数据值,并保存在栈中。所以b修改的时候,会根据地址回到a堆中修改,c则直接在栈中修改,并且不能指向a堆内存中。

3、浅拷贝

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

 1   var a = {
 2         key1:"11111"
 3     }
 4     function Copy(p) {
 5         var c = {};
 6         for (var i in p) {
 7           c[i] = p[i];
 8         }
 9         return c;
10   }
11     a.key2 = [‘小辉‘,‘小辉‘];
12     var b = Copy(a);
13    b.key3 = ‘33333‘;
14     alert(b.key1);     //1111111
15     alert(b.key3);    //33333
16     alert(a.key3);    //undefined

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

1 b.key2.push("大辉");
2 alert(b.key2);    //小辉,小辉,大辉
3 alert(a.key2);    //小辉,小辉,大辉

  但是,若修改的属性变为对象或数组时,那么父子对象之间就会发生关联。从以上弹出结果可知,我对b对象进行修改,a、b的key2属性值(数组)均发生了改变。其在内存的状态,可以用下图来表示。

  原因是key1的值属于基本类型,所以拷贝的时候传递的就是该数据段;但是key2的值是堆内存中的对象,所以key2在拷贝的时候传递的是指向key2对象的地址,无论复制多少个key2,其值始终是指向父对象的key2对象的内存空间。

4、深拷贝

  或许以上并不是我们在实际编码中想要的结果,我们不希望父子对象之间产生关联,那么这时候可以用到深拷贝。既然属性值类型是数组和或象时只会传址,那么我们就用递归来解决这个问题,把父对象中所有属于对象的属性类型都遍历赋给子对象即可。测试代码如下:

 1   function Copy(p, c) {
 2         var c = c || {};
 3         for (var i in p) {
 4           if (typeof p[i] === ‘object‘) {
 5              c[i] = (p[i].constructor === Array) ? [] : {};
 6              Copy(p[i], c[i]);
 7           } else {
 8              c[i] = p[i];
 9           }
10         }
11         return c;
12   }
13     a.key2 = [‘小辉‘,‘小辉‘];
14     var b={};
15     b = Copy(a,b);
16     b.key2.push("大辉");
17     alert(b.key2);    //小辉,小辉,大辉
18     alert(a.key2);    //小辉,小辉

  由上可知,修改b的key2数组时,没有使a父对象中的key2数组新增一个值,即子对象没有影响到父对象a中的key2。其存储模式大致如下:

说明:原创文章,转载请注明作者与出处,谢谢。

时间: 2024-10-11 06:58:58

关于JS堆栈与拷贝的相关文章

JS堆栈与拷贝

一.堆栈的定义 1.栈是一种特殊的线性表.其特殊性在于限定插入和删除数据元素的操作只能在线性表的一端进行. 结论:后进先出(Last In First Out),简称为LIFO线性表.栈的应用有:数制转换,语法词法分析,表达式求值等 2.队列(Queue)也是一种运算受限的线性表,它的运算限制与栈不同,是两头都有限制,插入只能在表的一端进行(只进不出),而删除只能在表的另一端进行(只出不进),允许删除的一端称为队尾(rear),允许插入的一端称为队头 (Front),队列的操作原则是先进先出的,

js堆栈溢出错误

function prompt() { var answer = prompt("What is your name","") if (answer) { alert("Hello, " + answer + "!"); } } 提示堆栈溢出.我就纳闷了. 反复测试之后,发现原来函数名不能命名为prompt. function prompttt() { var answer = prompt("What is you

js——堆栈

简单来说数据结构是计算机存储,组织数组的方式.数据结构是指相互之间存在一种或多种特定关系的数据元素的集合. 数组结构的分类:数组.栈.堆.队列.链表.树.图.散列表,本文主要介绍栈和堆. 一.定义 栈(Stack)又名堆栈,它作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表.它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据.栈具有记忆作用,对栈的插入与删除操作中,不需要改变栈底指针. 栈是允许在同一端进行插入和删除操作的特

js的深浅拷贝

js数据类型包括5种简单数据类型(或者基本数据类型):Undefined, Null, Number, String, Boolean, 指的是保存在栈内存中的简单数据段.还有一种复杂数据类型(引用数据类型):Object Function Array,指的是那些保存在堆内存中的对象. 深浅拷贝只是针对Object,Array这样的复杂对象.javascript存储对象都是存地址. 对象: 浅拷贝只是针对对象的各个属性进行一次复制,复制前后两个属性指向的还是同一块内存地址 深拷贝不仅对各个属性进

js数组的拷贝赋值复制二三事总结

今天在看React-native性能优化的时候,看到如何避免shouldComponentUpdate的异常数据时,脑内一阵风暴,从而牵连出一连串的问题,于是有了这一篇关于js数组的复制(深浅拷贝)与赋值等为何能产生异常数据的文章. 有什么问题欢迎指正 现在进入正题: 首先异常数据的产生在于我们在复制赋值时,会有或没有改变到本身的值. 一.push与concat push的定义是:像数组末尾添加一个或更多元素,并返回新的长度.该方法会改变数组的长度. concat的定义是:连接两个或更多的数组,

js堆栈内存的释放

### JS中的堆栈内存 > 俗称叫做作用域(全局作用域/私有作用域) > - 为js代码提供执行的环境(执行js代码的地方) > - 基本数据类型值是直接存放在栈内存中的 > **堆内存** > 存储引用数据类型值的(相当于一个存储的仓库) > - 对象存储的是键值对 > - 函数存储的是代码字符串 在项目中,我们的内存越少性能越好,我们需要把一些没用的内存处理掉 [堆内存] var o ={};当前对象对应的堆内存被变量o占用着呢,堆内存是无法销毁的. o =

js 数组的拷贝

在js中,数组Array是引用类型,直接将数组赋值给一个变量名,二者所指向的地址是一样的. 所以直接复制数组会产生意想不到的结构. 要想解决拷贝一个数组但是对副本的修改不影响原来的数组,有以下方式: //方法1 var a = [1,2,3]; var b = a.slice(); a.reverse; console.log(a);//[3,2,1] console.log(b);//[1,2,3] //方法2 var c = [4,5,6]; var d = c.concat(); c.re

js学习笔记<拷贝传值,引用传址和匿名函数>

拷贝传值:把一个变量的值拷贝一份,传给了另外一个变量拷贝传值中,两个变量之间没有任何联系,修改其中一个一个变量的值,原来的变量不变. 例: var arr1 = ["张三",24,"大专"]; //定义一个二维数组:   var arr2 = arr1;//将arr1的地址传给arr2,(不传递实际参数)   arr1[1] = 30;//改变arr1中的24等于30:   alert(arr2[1]);//输出arr2[1]; 引用传址: 例: var arr =

两行代码搞定js对象深浅拷贝

有一段时间没有更新博客了,忙于工作.2018年刚过去,今天来开启2018第一篇博文.好了,咱们步入正题. 先上代码 /** * 遍历对象 * 1.判断是不是原始值 * 2.判断是数组还是对象 * 3.建立相应的数组或对象 * 递归 */ var obj= { name:"aaa", age:23, hobby:["basketball","music","coding"], lover:{ name:"aaa&quo