javascript的浅复制和深复制

2017-10-12  本文已影响9人  id被吃

    javascript的基本类型包括字符串、数字、布尔、数组、对象、Null、Undefined。基本类型和对象最大的不同在于他们的传值方式:基本类型是按值传递的,但是对象是按引用传值的.
基本类型:

var a = 1;
var b = a;
b = 2;
console.log(a);  //1
console.log(b); //2

从上面的例子可以看出,由于是按值传递,所以改变b的值不会改变a
如果是对象,由于是按引用传值,类似的做法会改变另外一个相关对象的属性,这就是浅复制(新旧对象公用一块内存空间):

var obj1 = { a:1 , b:2 };
var obj2 = obj1;
obj.a = 3;
console.log(obj1);  //{ a:3 , b:2 }
console.log(obj2);  //{ a:3 , b:2 }
console.log(obj1 === obj2); //true

如果不让原本obj1对象属性,那么就是深复制(新旧对象使用不同内存空间)
简单实现:

var obj2 = { a:obj1.a , b:obj1.b };
obj2.a = 3;
console.log(obj1);  //{ a:1 , b:2 }
console.log(obj2);  //{ a:3 , b:2 }
console.log(obj1 === obj2); //false

这种方法可以实现深度复制,但是略显臃肿,而且如果有嵌套对象(有多层对象)实现起来就更麻烦了,比如:

var obj1 = { grade : { math : 100 , Chinese : 90 } };
var obj2 = { grade : obj1.grade };
obj2.grade.math = 120;
console.log(obj1);  // { grade : { math : 120 , Chinese : 90 } };  
var obj2 = { grade : obj1.grade.math , grade : obj1.grade.Chinese }; //这样才能深度复制

深度复制除了上面方法,还可以用其他方法实现

1.ES6的Object.assign

ES6引入了一个Object.assign的新函数,可以把任意个源对象自身的可枚举属性拷贝给目标对象,然后再返回目标对象。不过其进行的是浅复制i,复制的是对象属性的引用
不过,它还是可以实现一层的深度复制,比起前面的手动复制要简单一点,比如

var obj1 = { a:1 , b:2 };
var obj2 = Object.assign( {} , obj1 };
obj.a = 3;
console.log(obj1);  //{ a:1 , b:2 }
console.log(obj2);  //{ a:3 , b:2 }

2.JSON.stringify+JSON.parse

前面提到了,js的基本类型是按值传递的,那么既然有这样的特性我们完全可以将对象转换成字符串,然后再用parse解析成新对象

var obj1 = { a:1 , b:2 };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj.a = 3;
console.log(obj1);  //{ a:1 , b:2 }
console.log(obj2);  //{ a:3 , b:2 }
console.log(obj1 === obj2); //false

不过,这个方法还是有缺陷,只有可以转换成JSON格式的对象才可以使用,RegExp对象是无法通过该方法实现深度复制的,函数也无法使用

3.递归拷贝

function deepClone(initalObj,finalObj){
    var obj = finalObj || {};
    for(var i  in initalObj){
        var prop = initalObj[i];
        if(prop === obj)
          continue;
        if(typeof prop === 'object'){
            obj[i] = (prop.constructor === Array) ? [] : {};
            arguments.callee(prop,obj[i]);
        }else{
            obj[i] = prop;
        }
    }
    return obj;
}

4.Object.create(initalObj)

注意和前面递归方法的区别

function deepClone(initalObj,finalObj){
    var obj = finalObj || {};
    for(var i  in initalObj){
        var prop = initalObj[i];
        if(prop === obj)
          continue;
        if(typeof prop === 'object'){
            obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
            arguments.callee(prop,obj[i]);
        }else{
            obj[i] = prop;
        }
    }
    return obj;
}

5.slice和concat巧妙的方法

实质上这也是浅复制,只不过返回一个浅复制了原数组中的元素的一个新数组

var arr1 = [1, 2, 3, 4],
    arr2 = arr1.slice(0),
    arr3 = arr1.concat();
 
console.log(arr1, arr2, arr3);
arr2[2] = 10;
arr3[2] = 11;
console.log(arr1[2], arr2[2], arr3[2]);
> 1,2,3,4, 1,2,3,4, 1,2,3,4
> 3, 10, 11

console.log( arr1 === arr2 ); //false
console.log( arr1 === arr3 ); //false

又比如:

var array = [1, [1,2,3], {name:"array"}]; 
var array_concat = array.concat();
var array_slice = array.slice(0);
array_concat[1][0] = 5;  //改变array_concat中数组元素的值 
console.log(array[1]); //[5,2,3] 
console.log(array_slice[1]); //[5,2,3] 
array_slice[2].name = "array_slice"; //改变array_slice中对象元素的值 
console.log(array[2].name); //array_slice
console.log(array_concat[2].name); //array_slice

jQuery提供了一个$.extend方法可以做深度复制

var arr1 = [1, 2, [3, 4], {a: 5, b: 6}, 7],
    arr2 = $.extend(true, [], arr1);
console.log(arr1, arr2);
arr2[1] = 10;
console.log(arr1, arr2);

还有一个叫lodash的函数库的_.cloneDeep方法可以实现深度复制

参考:http://www.cnblogs.com/Chen-XiaoJun/p/6217373.html
http://web.jobbole.com/88602/
https://github.com/wengjq/Blog/issues/3

上一篇下一篇

猜你喜欢

热点阅读