js中的深拷贝与浅拷贝

一、如何区分深拷贝和浅拷贝

1.图示区分:

image
image

2.代码区分:

(1)浅拷贝示例:

let family = {
    age: 32,
    name: 'yorick',
    children: [
        {
            age: 3,
            name: 'moon'
        },
        {
            age: 1,
            name: 'flower'
        }
    ]
};
let family1 = family;
family1.name = 'jack';
family1.children[0] = {
    age: 10,
    name: 'lily'
};
console.log("family.name", family.name);
// yorick
console.log("family.children[0]", family.children[0]);
/* 
    {
        age: 10,
        name: 'lily'
    }
*/
console.log("family1.name", family1.name);
// jack
console.log("family1.children[0]", family1.children[0]);
/* 
    {
        age: 10,
        name: 'lily'
    }
*/

(2)浅拷贝的含义:重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会相互影响。

// 浅拷贝的实现方法
const shallowClone = sourceObj => {
    const targetObj = {};
    for(let i in sourceObj) {
        if (sourceObj.hasOwnProperty(i)) {
            targetObj[i] = sourceObj[i];
        }
    }
    return targetObj;
}

(3)深拷贝的示例:

let family = {
    age: 32,
    name: 'yorick',
    children: [
        {
            age: 3,
            name: 'moon'
        },
        {
            age: 1,
            name: 'flower'
        }
    ]
};
const deepClone = sourceObj => {
    // 处理一些异常输入
    if (sourceObj === null) return sourceObj; 
    if (sourceObj instanceof RegExp) return new RegExp(sourceObj);
    if (sourceObj instanceof Date) return new Date(sourceObj);
    if (typeof sourceObj !== "object") return sourceObj;
    // 递归法实现深拷贝
    let targetObj = new sourceObj.constructor();
    for (let key in sourceObj) {
      if (sourceObj.hasOwnProperty(key)) {
        // 实现一个递归拷贝
        targetObj[key] = deepClone(sourceObj[key]);
      }
    }
    return targetObj;
}
let family1 = deepClone(family);
family1.name = 'jack';
family1.children[0] = {
    age: 10,
    name: 'lily'
};
console.log("family.name", family.name);
// yorick
console.log("family.children[0]", family.children[0]);
/* 
    {
        age: 3,
        name: 'moon'
    }
*/
console.log("family1.name", family1.name);
// jack
console.log("family1.children[0]", family1.children[0]);
/* 
    {
        age: 10,
        name: 'lily'
    }
*/

(4)深拷贝的含义:从堆内存中开辟一个新的区域存放新对象,对对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响。

// 深拷贝的递归实现方法
const deepClone = sourceObj => {
    // 处理一些异常输入
    if (sourceObj === null) return sourceObj; 
    if (sourceObj instanceof RegExp) return new RegExp(sourceObj);
    if (sourceObj instanceof Date) return new Date(sourceObj);
    if (typeof sourceObj !== "object") return sourceObj;
    // 递归法实现深拷贝
    let targetObj = new sourceObj.constructor();
    for (let key in sourceObj) {
      if (sourceObj.hasOwnProperty(key)) {
        // 实现一个递归拷贝
        targetObj[key] = deepClone(sourceObj[key]);
      }
    }
    return targetObj;
}

(5)关于对象赋值操作与深浅拷贝的区别:当我们把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。

二、浅拷贝和深拷贝的一些常见实现方式

1.浅拷贝的实现方式:

(1)Object.assign()
划重点:面试常常问到这个问题,Object.assign()方法是可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。
(2)展开运算符...
这两个就合并举例了

let family = {
    age: 32,
    name: 'yorick',
    children: [
        {
            age: 3,
            name: 'moon'
        },
        {
            age: 1,
            name: 'flower'
        }
    ]
};
let family1 = {
    age: 28,
    name: 'jason',
    children: [
        {
            age: 5,
            name: 'amy'
        }
    ]
};
// 效果一样,都是浅拷贝
const newFamily1 = {...family, ...family1};
const newFamily2 = Object.assign({}, family, family1);

(3)函数库lodash的_.clone方法

var _ = require('lodash');
let family = {
    age: 32,
    name: 'yorick',
    children: [
        {
            age: 3,
            name: 'moon'
        },
        {
            age: 1,
            name: 'flower'
        }
    ]
};
var family1 = _.clone(family);
var family2 = _.cloneDeep(family);
console.log(family.children[0].age === family1.children[0].age);  // true
console.log(family.children[0].age === family2.children[0].age);  // false

2.深拷贝的实现方式:

(1) JSON.parse(JSON.stringify())
(2)函数库lodash的_.cloneDeep方法
这个例子放在上边了。
(3)jQuery.extend()方法

//第一个参数为true,就是深拷贝
$.extend(isDeepCopy, target, object1, [objectN])

(4)手写递归方法,上边已经提供,不再赘述。
后记:以上的深拷贝方法都有一些局限性,(1)解决不了循环引用的问题,(2)(3),如果你做的是比较新的前端项目,有可能不会引入这两个库,个人目前在项目中用的是(4)的方法,但是这种方法针对大对象是否有优化空间是个值得思考的问题,当我在项目中实践以后会分享出来的。

你可能感兴趣的:(js中的深拷贝与浅拷贝)