首页 > 技术文章 > 深拷贝和浅拷贝

jiajia-hjj 2021-09-15 12:37 原文

深拷贝和浅拷贝

  • 值类型的拷贝的是拷贝值的副本,都互不影响。
  • 深拷贝和浅拷贝是针对引用类型(如数组、对象)的拷贝。

一、浅拷贝

  • 引用类型拷贝的是“引用“(即指针),之后一方改变都会影响到到另一方。

两种情况

  • 情况一:直接赋值,直接拷贝源的是对象(数组)的引用,相互影响。

  • 情况二:不是直接赋值(拷贝源对象的实例)

    • 属性值是值类型时,直接拷走值的副本,两个都互不影响;

    • 属性值的引用类型时,拷贝的是引用(地址),一方改变都会影响到到另一方。

实现

对象拷贝:

const obj = {
    name:"hjj",
    info:{
        age:18,
        props:["阳光","开朗"]
    }
}
//1. ...实现
let copy1={...obj}
console.log(copy1===obj)//false
console.log(copy1.info === obj.info);// true
copy1.info.age=181;
console.log(obj.info.age);//181
copy1.name='HHH1';
console.log(copy1.name,obj.name);//HHH1 hjj


// 2. Object.assign实现
let copy2=Object.assign({},obj)
console.log(copy2===obj)//false
console.log(copy2.info === obj.info);// true
copy2.info.age=182;
console.log(obj.info.age);//182
copy2.name='HHH2';
console.log(copy2.name,obj.name);//HHH2 hjj

// 3. for in实现
let copy3={}
for(let key in obj){
    copy3[key]=obj[key]
}
console.log(copy3===obj)//false
console.log(copy3.info === obj.info);// true
copy3.info.age=183;
console.log(obj.info.age);//183
copy3.name='HHH3';
console.log(copy3.name,obj.name);//HHH3 hjj

//4.直接赋值,直接拷贝源对象的引用
let copy4=obj
console.log(copy4===obj)//true
console.log(copy4.info === obj.info);// true
copy4.info.age=184;
console.log(obj.info.age);//184
copy4.name='HHH4';
console.log(copy4.name,obj.name);//HHH4 HHH4

数组拷贝:

const arr=[
    {
        name:"hjj",
        info:{
            age:18,
            props:["阳光","开朗"]
        }
    },
    0
]
//1. ...实现
let copy1=[...arr]
console.log(copy1===arr);//false
console.log(copy1[0].info === arr[0].info);// true
copy1[0].info.age=181;
console.log(arr[0].info.age);//181
copy1[1]=1
console.log(copy1[1],arr[1]);//1 0

//2.Array.from实现
let copy2=Array.from(arr);
console.log(copy2===arr);//false
console.log(copy2[0].info === arr[0].info);// true
copy2[0].info.age=182;
console.log(arr[0].info.age);//182
copy2[1]=2
console.log(copy2[1],arr[1]);//2 0


// 3、forEach实现
let copy3 = [];
arr.forEach(item=>copy3.push(item));
copy3 = Array.from(arr);
console.log(copy3 === arr);// false
console.log(copy3[0].info === arr[0].info);// true
copy3[0].info.age=183;
console.log(arr[0].info.age);//183
copy3[1]=3
console.log(copy3[1],arr[1]);//3 0

// 4、map实现
let copy4 = arr.map(item=>item);
console.log(copy4 === arr);// false
console.log(copy4[0].info === arr[0].info);// true
copy4[0].info.age=184;
console.log(arr[0].info.age);//184
copy4[1]=4
console.log(copy4[1],arr[1]);//4 0

//5、slice()
let copy5=arr.slice()
console.log(copy5===arr)//true
console.log(copy5[0].info === arr[0].info);// true
copy5[0].info.age=185;
console.log(arr[0].info.age);//185
copy5[1]=5
console.log(copy5[1],arr[1]);//5 0

//6.直接赋值,直接拷贝源数组的引用
let copy6=arr
console.log(copy6===arr)//true
console.log(copy6[0].info === arr[0].info);// true
copy6[0].info.age=186;
console.log(arr[0].info.age);//186
copy6[1]=6
console.log(copy6[1],arr[1]);// 6 6

二、深拷贝

  • 创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,拷贝的是“值”而不是“引用”。
  • 改变新的数组(对象)的时候,不会改变原数组(对象)。互不影响

实现

方法一:JSON.parse(JSON.stringify(obj))

  • 局限:函数不能复制;原型链搞没了...
const obj = {
    name:"hjj",
    info:{
        age:18,
        props:["阳光","开朗"]
    }
}
let copy1=JSON.parse(JSON.stringify(obj));
console.log(copy1===obj)//false
console.log(copy1.info === obj.info);// false
copy1.info.age=181;
console.log(obj.info.age);//18

方法二:递归拷贝

普通版本

/**
 * 深拷贝 普通递归版本
 * @param {Object} obj 要拷贝的对象
 */
function deepClone(obj){
    // 判断是否数组或对象,确定初始值
    let copy = obj instanceof Array ? [] : {}
    for (let i in obj) {

        if (obj.hasOwnProperty(i)) {
            console.log(obj.hasOwnProperty(i),i)
            copy[i] = typeof obj[key]==="object"? deepClone(obj[i]) : obj[i]
        }
    }
    return copy
}


function deepClone(obj) {
    if (typeof obj !== "object"||obj==null) {
        return obj;
    }
    // 判断是否数组或对象,确定初始值
    let result = (obj instanceof Array || Object.prototype.toString.call(obj)=== "[object Array]")?[]:{};
    for(let key in obj){
        // 保证 key 不是原型属性
        if(obj.hasOwnProperty(key)){
            result[key]=deepClone(obj[key]);
        }
    }
    return result;
}
//调试1
const obj = {
    name:"hjj",
    info:{
        age:18,
        props:["阳光","开朗"]
    }
}
deepClone(obj);
//调试2:循环引用会出现死循环问题
let obj={name:'hjj'}
obj.per=obj
deepClone(obj)

解决循环引用问题的版本

/** 
 * 深拷贝 解决循环引用问题的版本
 * @param {Object} obj 要拷贝的对象
 * @param {Map} map 用于存储循环引用对象的地址
 */

function deepClone(obj = {}, map = new Map()) {
    if (typeof obj !== "object") {
        return obj;
    }
    if (map.get(obj)) {
        return map.get(obj);
    }

    let result = {};
    // 初始化返回结果
    if (
        obj instanceof Array ||
        // 加 || 的原因是为了防止 Array 的 prototype 被重写,Array.isArray 也是如此
        Object.prototype.toString.call(obj) === "[object Array]"
    ) {
        result = [];
    }
    // 防止循环引用
    map.set(obj, result);
    for (const key in obj) {
        // 保证 key 不是原型属性
        if (obj.hasOwnProperty(key)) {
            // 递归调用
            result[key] = deepClone(obj[key], map);
        }
    }
    // 返回结果
    return result;
}

//调试1
const obj = {
    name:"hjj",
    info:{
        age:18,
        props:["阳光","开朗"]
    }
}
deepClone(obj);
//调试2 循环引用
let obj={name:'hjj'}
obj.per=obj
deepClone(obj)

**方法:jQuery.extend() **

  • $.extend( [deep], target, object1 [, objectN ] )
  • 用于将一个或多个对象内容合并到目标对象
  • deep:默认false-浅拷贝,true-深拷贝
var a = [{m:1}, {n:2},3]; 
var b =[];
$.extend(true,b,a);
console.log(a === b);// false
a[0].m = 4;
console.log(b[0].m)//1

参考转自:

javascript总结:深拷贝与浅拷贝的实现

推荐阅读