你的位置:首页 > Java教程

[Java教程]关于JS堆栈与拷贝


1、栈(stack)堆(heap)

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

2、基本类型和引用类型

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

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

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

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

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]);//610   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);   //111111115   alert(b.key3);  //3333316   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);  //小辉,小辉,大辉

  如果修改的属性变为对象或数组,那么父子对象就会发生关联。从以上弹出结果可知,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。堆内存变化如下:

 

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