Skip to content

深拷贝和浅拷贝

浅拷贝

只复制最外一层,里面的都还是相同引用

  • 如果拷贝的是基本数据类型,拷贝的就是基本数据类型的值

  • 如果拷贝的是引用数据类型,拷贝的仅仅只是内存地址(引用)

Object.assign

Object.assign,将所有可枚举(Object.propertyIsEnumerable() 返回 true)的自有属性从一个或多个源对象复制到目标对象,返回修改后的对象

js
Object.assign(target, ...sources)

扩展运算符

使用扩展运算符可以在构造字面量对象的时候,进行属性的拷贝

js
let cloneObj = { ...obj };

数组浅拷贝

Array.prototype.slice

slice()方法是JavaScript的数组方法,该方法可以从已有数组中返回选定的元素,不会改变原始数组

js
array.slice(start, end)

slice方法不会修改原数组,只会返回一个浅拷贝了原数组中的元素的一个新数组。

如果向两个数组任一中添加了新元素,则另一个不会受到影响

Array.prototype.concat

concat()方法用于合并两个或多个数组,此方法不会更改原始数组,而是返回一个新数组

js
arrayObject.concat(arrayA,arrayB,...,arrayZ)

如果省略了所有参数,会返回调用此方法的现存数组的一个浅拷贝(新数组)

手写实现

js
function shallowCopy(params){

    if(!params || typeof params!=="object")return params;

    let newObject = Array.isArray(params) ? [] : {};

    for(let key in params){
        if(params.hasOwnProperty(key)){
            newObject[key]=params[key];
        }
    }

    return newObject;
}

深拷贝

JSON.stringify

原理是利用JSON.stringify将JavaScript对象序列化成为JSON字符串,并将对象里面的内容转换为字符串,再使用JSON.parse来反序列化,将字符串生成一个新的JavaScript对象

js
let obj1={
    a:0,
    b:{
        c:0
    }
};

let obj2=JSON.parse(JSON.stringigy(obj1))l
obj1.a=1;
obj2.b.c=1;
console.log(obj1);
console.log(obj2);

问题:

  • 拷贝的对象中如果有function、undefined、symbol,当使用JSON.stringigy()进行处理之后,都会消失

  • 无法拷贝不可枚举的属性

  • 无法拷贝对象的原型链

  • 拷贝Date引用类型会变成字符串

  • 拷贝RegExp引用类型会变成空对象

  • 对象中含有NaN,Infinity以及-Infinity,JSON序列化的结果会变成null

  • 无法拷贝对象的循环应用,即对象成环(obj[key]=obj)

函数库loadsh

提供_.cloneDeep()来做深拷贝

js
var _=require('lodash');

var obj1={
    a:1,
    b:{f:{g:1}},
    c:[1,2,3]
};

var obj2=_.cloneDeep(obj1);

console.log(obj1.b.f===obj2.b.f);//false

手写深拷贝

js
function deepClone(source,map=new Map()){
    if(source instanceof Object === false)return source;

    let target=Array.isArray(source) ? [] : {};

    /* -----------解决环引用爆栈问题-------------- */
    if(map.get(source)){
        // 已存在则直接返回
        return map.get(source);
    }

    // 不存在则第一次设置
    map.set(source,target);


    for(let i in source){
        if(source.hasOwnProperty(i)){
            if(typeof source[i]==='object'){
                // 传递map
                target[i]=deepClone(source[i],map);
            }else{
                target[i]=source[i];
            }
        }
    }
}