大家好,我是IT修真院成都分院第7期的学员韩建名,一枚正直纯洁善良的WEB前端程序员。
1.背景介绍
在使用JavaScript对数组进行操作的时候,我们经常需要将数组进行备份,事实证明如果只是简单的将它赋予其他变量,那么我们只要更改其中的任何一个,然后其他的也会跟着改变,这就导致了问题的发生。
var arr = ["One","Two","Three"]; var arrto = arr; arrto[1] = "test"; document.writeln("数组的原始值:" + arr + "
"); //Export:数组的原始值:One,test,Three document.writeln("数组的新值:" + arrto + "
"); //Export:数组的新值:One,test,Three
像上面的这种直接赋值的方式就是浅拷贝,很多时候,这样并不是我们想要得到的结果,其实我们想要的是arr的值不变
2.知识剖析
什么是浅拷贝?
什么是深拷贝?
浅拷贝
浅拷贝就是把父对像的属性,全部拷贝给子对象。
但是,这样的拷贝有一个问题。那就是,如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能。
只是拷贝了基本类型的数据,我们把这种拷贝叫做“浅拷贝”。
深拷贝
所谓”深拷贝”,就是能够实现真正意义上的数组和对象的拷贝。它的实现并不难,只要递归调用”浅拷贝”就行了。
3.常见问题
如何实现深拷贝?
方法1
js的slice函数
对于array对象的slice函数, 返回一个数组的一段。(仍为数组) arrayObj.slice(start, [end]) 参数 arrayObj 必选项。一个 Array 对象。 start 必选项。 arrayObj 中所指定的部分的开始元素是从零开始计算的下标。 end 可选项。 arrayObj 中所指定的部分的结束元素是从零开始计算的下标。 说明 slice 方法返回一个 Array 对象,其中包含了 arrayObj 的指定部分。 slice 方法一直复制到 end 所指定的元素,但是不包括该元素。如果 start 为负,将它作为 length + start处理,此处 length 为数组的长度。如果 end 为负,就将它作为 length + end 处理,此处 length 为数组的长度。如果省略 end ,那么 slice 方法将一直复制到 arrayObj 的结尾。如果 end 出现在 start 之前,不复制任何元素到新数组中。
var arr = ["One","Two","Three"]; var arrtoo = arr.slice(0); arrtoo[1] = "set Map"; document.writeln("数组的原始值:" + arr + "
"); //Export:数组的原始值:One,Two,Three document.writeln("数组的新值:" + arrtoo + "
"); //Export:数组的新值:One,set Map,Three
方法2
js的concat方法
concat() 方法用于连接两个或多个数组。该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。 语法 arrayObject.concat(arrayX,arrayX,......,arrayX) 说明 返回一个新的数组。该数组是通过把所有 arrayX 参数添加到 arrayObject 中生成的。如果要进行 concat() 操作的参数是数组,那么添加的是数组中的元素,而不是数组。
var arrtooo = arr.concat(); arrtooo[1] = "set Map To"; document.writeln("数组的原始值:" + arr + "
"); //Export:数组的原始值:One,Two,Three document.writeln("数组的新值:" + arrtooo + "
"); //Export:数组的新值:One,set Map To,Three
4.编码实战
请对以上示例代码加以尝试
5.扩展思考
(1)还有哪些方法可以实现数组的深拷贝
(2)深拷贝和浅拷贝的原理是什么
了解引用类型和基本类型
按照CEMA-262第3版的定义,js变量松散类型的本质,决定了它只是在特定时间用于保存特定值的一个名字而已。由于不存在必须定义变量数据类型的规则,变量的值及其数据类型可以在脚本生命周期内改变。尽管从某种角度看,这可能是一个既有趣又强大,同时又容易出问题的特性,但js变量实际的复杂程度还远不止如此。 ECMAScript变量可能包含两种不同数据类型的值:基本类型值和引用类型值。 基本类型值指的是那些保存在栈内存中的简单数据段,即这种值完全保存在内存中的一个位置。 而引用类型值则是指那些保存在堆内存中的对象,意思是变量中保存的实际上只是一个指针,这个指针指向内存中的另一个位置,该位置保存对象。
在将一个值赋给变量时,解析器必须确定这个值是基本类型值,还是引用类型值。(Undefined、Null、Boolean、Number、String)这五种基本数据类型的值在内存中分别占有固定大小的空间,因此可以把他们的值保存在栈内存中。对于保存基本数据类型的变量,我们可以说他们是按值访问的,因为我们操作的是它们实际保存的值。 如果赋给变量的是一个引用类型的值,则必须在堆内存中去为这个值分配空间。由于这种值的大小不固定,因此不能把它们保存到栈内存中。但内存地址的大小是固定的,因此可以将内存地址保存在栈内存中。这样,当查询引用类型的变量时,就可以首先从栈中读取内存地址,然后在“顺藤摸瓜”找到保存在堆中的值。对于这种查询方式,我们把它叫做按引用访问,因为我们操作的不是实际的值,而是被那个值所引用的对象。
6.参考文献
Javascript 高级程序设计
7问题讨论
Q : 王帅:slice方法可以支持倒着操作吗,如何实现?
A:可以实现,倒着切片需要把参数设置为负数。
Q :常开洋:函数的参数是通过何种方式传递?
A :按值传递。
数组的浅拷贝和深拷贝_腾讯视频
网友评论