js深拷贝和浅拷贝的区别

JavaScript039

js深拷贝和浅拷贝的区别,第1张

1.浅拷贝:复制一份引用,所有引用对象都指向一份数据,并且都可以修改这份数据。

2.深拷贝(复杂):复制变量值,对于非基本类型的变量,则递归至基本类型变量后,再复制。

一、数组的深浅拷贝

在使用JavaScript对数组进行操作的时候,我们经常需要将数组进行备份,事实证明如果只是简单的将它赋予其他变量,那么我们只要更改其中的任何一个,然后其他的也会跟着改变,这就导致了问题的发生。

var arr = ["One","Two","Three"] var arrto = arr arrto[1] = "test" document.writeln("数组的原始值:" + arr + "<br />")//Export:数组的原始值:One,test,Three document.writeln("数组的新值:" + arrto + "<br />")//Export:数组的新值:One,test,Three

像上面的这种直接赋值的方式就是浅拷贝,很多时候,这样并不是我们想要得到的结果,其实我们想要的是arr的值不变,不是吗?

方法一:js的slice函数

var arr = ["One","Two","Three"] var arrtoo = arr.slice(0) arrtoo[1] = "set Map" document.writeln("数组的原始值:" + arr + "<br />")//Export:数组的原始值:One,Two,Three document.writeln("数组的新值:" + arrtoo + "<br />")//Export:数组的新值:One,set Map,Three

方法二:js的concat方法

var arr = ["One","Two","Three"] var arrtooo = arr.concat() arrtooo[1] = "set Map To" document.writeln("数组的原始值:" + arr + "<br />")//Export:数组的原始值:One,Two,Three document.writeln("数组的新值:" + arrtooo + "<br />")//Export:数组的新值:One,set Map To,Three

二、对象的深浅拷贝

var a={name:'yy',age:26} var b=new Object() b.name=a.name b.age=a.age a.name='xx' console.log(b)//Object { name="yy", age=26} console.log(a)//Object { name="xx", age=26}

就是把对象的属性遍历一遍,赋给一个新的对象。

var deepCopy= function(source) { var result={} for (var key in source) { result[key] = typeof source[key]==='object'? deepCoyp(source[key]): source[key] }return result }

举一个jQuery中的例子:

jQuery.extend = jQuery.fn.extend = function() {//1.将extend方法扩展到JQ(函数)下边:扩展静态方法 //2. jQuery.fn.extend 把extend扩展到jq.fn下 且jQuery.fn = jQuery.prototype 扩展实例方法 // 1.2.功能相似 var options, name, src, copy, copyIsArray, clone, //定义一些变量 target = arguments[0] || {},//目标元素是【0】第一个元素$.extend( a , { name : 'hello' } , { age : 30 } ) i = 1, //第一个元素的位置 length = arguments.length,//第一个个对象的元素 deep = false//是否是深拷贝 默认 false不是 // Handle a deep copy situation 看是不是深拷贝情况 if ( typeof target === "boolean" ) { //是布尔值deep = target target = arguments[1] || {}//目标元素是第二个$.extend( true , a , b ) // skip the boolean and the target i = 2 } // Handle case when target is a string or something (possible in deep copy) 看参数正确不 if ( typeof target !== "object" &&!jQuery.isFunction(target) ) { // 当目标不是对象或者不是函数的时候 target = {}//变成一个空的jason } // extend jQuery itself if only one argument is passed看是不是插件情况 if ( length === i ) { //只写了一个对象 要把这个对象扩展到jq源码上 静态方法 或者是实例方法 target = this//this 是$ 或者 $() --i } // 可能有多个对象情况 for ( i <lengthi++ ) { // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) {//看后边的对象是否都有值 // Extend the base object for ( name in options ) { src = target[ name ] copy = options[ name ]// Prevent never-ending loop if ( target === copy ) {//防止循环引用 continue//跳出本次循环继续执行// $.extend( a , { name : a } ) )循环引用 a也是一个对象 } // Recurse if we're merging plain objects or arrays深拷贝 if ( deep &&copy &&( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {// 是深拷贝 且需有var b = { name : { age : 30 } }且b必须是对象自变量(jason) 或者是个数组//递归if ( copyIsArray ) { //数组copyIsArray = false clone = src &&jQuery.isArray(src) ? src : []//定义一个空数组 } else {//jasonclone = src &&jQuery.isPlainObject(src) ? src : {}//看原有的属性有没有且是不是jason定义一个空jason}// var a = { name : { job : 'it' } }看有没有原有的属性 有的话在原有的上边添加// var b = { name : {age : 30} } // $.extend( true , a , b )//a继承b// console.log( a )a{ name:{ job : 'it' ,age : 30}} 如果只有一个{} 则只有,age : 30// Never move original objects, clone(a) themtarget[ name ] = jQuery.extend( deep, clone, copy )//调用函数本身进行进一步的递归处理 // Don't bring in undefined values浅拷贝 } else if ( copy !== undefined ) {target[ name ] = copy//直接复制因为里边没有对象 } } } } // Return the modified object return target }

简单来说,深拷贝主要是将另一个对象的属性值拷贝过来之后,另一个对象的属性值并不受到影响,因为此时它自己在堆中开辟了自己的内存区域,不受外界干扰。

浅拷贝主要拷贝的是对象的引用值,当改变对象的值,另一个对象的值也会发生变化。

使用 js ON.stringify和 js ON.parse实现深拷贝:JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象;

缺陷:它会抛弃对象的 const ructor,深拷贝之后,不管这个对象原来的构造 函数 是什么,在深拷贝之后都会变成Object;这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,也就是说,只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON;

递归拷贝实现深拷贝,解决循环引用问题