深拷贝、浅拷贝及其实现方式

发布于:2022-12-28 ⋅ 阅读:(487) ⋅ 点赞:(0)


一、浅拷贝

  浅拷贝和深拷贝都只针对于引用数据类型,浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存
例如:

const obj_1 = {
    age: 21
}
const obj_2 = obj_1;    // 对象obj_2只复制了obj_1的地址
obj_2.age = 19;
console.log(obj_1.age);     // 输出:19

内存角度:对象obj_1会在中分配一个内存地址,该内存地址对应的值又会创建一个新的内存地址,该新的内存地址又指向这样一个数据结构,并在堆中创建对应的属性值age:21。而上述案例中创建obj_2并赋值obj_1,实际上只复制了指针,这么做将会导致obj_2与obj_1指向同一个地址,他们两从栈到堆这两种数据类型的链路都是一样的,obj_2修改属性age的值,导致obj_1中age的值发生改变,称作只实现了浅拷贝。

二、深拷贝

  不同于浅拷贝,深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象;

三、判断深、浅拷贝

  我们通过我们熟知的引用类型——数组,用其内置的一些方法判断是浅拷贝还是深拷贝

1.concat()方法

判断:

let list = ['我爱', '学习'];
let copyList = [].concat(list);
copyList.push('JavaScript');
console.log(list);      // ['我爱', '学习']
console.log(copyList);      // ['我爱', '学习', 'JavaScript']

  我们使用concat()方法拷贝副本,返回新构建的数组,此时我对新数组元素的添加也不会影响到原数组,然而。。。答案为:浅拷贝

2.slice()方法

判断:

let list = ['我爱', '学习'];
let copyList = list.slice();
copyList.push('JavaScript');
console.log(list);      // ['我爱', '学习']
console.log(copyList);      // ['我爱', '学习', 'JavaScript']

  我们使用slice()方法不传参的方式从头到尾拷贝副本,并且返回新数组,即使我对新数组添加元素也不会影响到原数组,然而。。。答案仍然为:浅拷贝

3.Array.from()方法

判断:

let list = ['我爱', '学习'];
let copyList = Array.from(list);
copyList.push('JavaScript');
console.log(list);      // ['我爱', '学习']
console.log(copyList);      // ['我爱', '学习', 'JavaScript']

  我们使用ES6中Array.from()方法同样以不影响原数组的方式实现了拷贝,然而。。。答案依旧为:浅拷贝

4.扩展运算符

判断:

let list = ['我爱', '学习'];
let copyList = [...list];
copyList.push('JavaScript');
console.log(list);      // ['我爱', '学习']
console.log(copyList);      // ['我爱', '学习', 'JavaScript']

  我们再次使用ES6中扩展运算符,同理:浅拷贝

我们来考证一下:

let list = [{ name: 'Tom' }];
let copyList = [...list];
copyList[0].name = 'Jerry'
console.log(list[0].name);      // Jerry
console.log(copyList[0].name);      // Jerry

  我们将数组元素都换成引用类型,执行以上方法,并通过拷贝副本改变数组元素的值,我们会发现原数组的元素也发生了改变,由此可见,确实是浅拷贝。除此之外,数组的其他方法如map()、filter()、reduce(),都是会造成浅拷贝的。

四、实现浅拷贝

1.Object.assign方法

let list = {
    obj: {
        hobby: 'JavaScript'
    }
}
let copyList = Object.assign({}, list);
copyList.obj.hobby = 'Vue'
console.log(list.obj.hobby);    // Vue

2.for in方法

let user1 = {
    name: "小明",
    age: 30,
    height: 1.9,
    hobby: {
        project: 'React'
    }
};
let user2 = {};
for (let i in user1) {
    user2[i] = user1[i];    //user1[i]输出的是每一个属性的值,将user1的每一个属性的值取出来赋值给user2对象
}
user2.name = '小蓝'
user2.hobby.project = 'Vue'
console.log(user1)      //{ name: "小明", age: 30, height: 1.9, hobby: { project: 'Vue' } }

五、实现深拷贝

1.递归实现

采用递归去拷贝所有层级属性

function deepClone(obj) {
    let objClone = Array.isArray(obj) ? [] : {};
    if (obj && typeof obj === "object") {
        for (key in obj) {
            if (obj.hasOwnProperty(key)) {
                //判断ojb子元素是否为对象,如果是,递归复制
                if (obj[key] && typeof obj[key] === "object") {
                    objClone[key] = deepClone(obj[key]);
                } else {
                    //如果不是,简单复制
                    objClone[key] = obj[key];
                }
            }
        }
    }
    return objClone;
}
let a = [1, 2, 3, 4],
    b = deepClone(a);
a[0] = 2;
console.log(a, b)	// [2,2,3,4]  [1,2,3,4]

2.JSON.stringify与JSON.parse

通过JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成一个全的新的对象;

let list = [{ name: 'Tom' }];
let copyList = JSON.parse(JSON.stringify([...list]));
copyList[0].name = 'Jerry'
console.log(list[0].name);      // Tom
console.log(copyList[0].name);      // Jerry

3.loadash函数库

函数库lodash,也有提供_.cloneDeep用来做深拷贝

let lodash = require('lodash');
let list = [{ name: 'Tom' }];
let copyList = lodash.cloneDeep(list);
copyList[0].name = 'Jerry'
console.log(list[0].name);      // Tom
console.log(copyList[0].name);      // Jerry

本文含有隐藏内容,请 开通VIP 后查看