js深浅拷贝

100 阅读4分钟

1.浅拷贝

浅拷贝是指,一个新的对象对原始对象的属性值进行精确地拷贝,如果拷贝的是基本数据类型,拷贝的就是基本数据类型的值(不会互相影响);如果拷贝的是引用数据类型,拷贝的就是内存地址。如果其中一个对象的引用内存地址发生改变,另一个对象也会发生变化。

浅拷贝对象方法:

1.Object.assign: es6中Object提供的合并对象的方法:

let target = {a: 1};  //目标对象可以为{}
// 要拷贝的对象
let object2 = {b: {d : 2}}; 
let object3 = {c: 3}; 
Object.assign(target, object2, object3);
console.log(target); // {a: 1, b: {d : 2}, c: 3}

注意点:

  • 如果目标对象和源对象有同名属性,或者多个源对象有同名属性,则后面的属性会覆盖前面的属性;

  • 如果该函数只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,会先将参数转为对象然后返回;

  • 因为nullundefined 不能转化为对象,所以第一个参数不能为nullundefined,否则会报错;

  • 它不会拷贝对象的继承属性,不会拷贝对象的不可枚举的属性,可以拷贝 Symbol 类型的属性。 2.扩展运算符:
    扩展运算符可以在构造字面量对象的时候,进行属性的拷贝。

let obj1 = {a:1,b:{c:1}} 
let obj2 = {...obj1};
// 修改obj1中的基础类型a的值
obj1.a = 2;
console.log(obj1);    //{a:2,b:{c:1}}
console.log(obj2);    //{a:1,b:{c:1}} a是基础类型,不会被影响
obj1.b.c = 2;
console.log(obj1);    //{a:2,b:{c:2}} 
console.log(obj2);     //{a:1,b:{c:2}} b为引用类型 源对象司改后也跟着改变

3.手写浅拷贝:

 // 浅拷贝的实现;
            function simpleClone(obj) {
                // 只拷贝对象
                if (obj && typeof obj !== 'object') return
                // 根据 obj 的类型判断是新建一个数组还是对象
                let newObject = Array.isArray(obj) ? [] : {}
                // 遍历 obj,只拷贝自身属性
                for (let key in obj) {
                    if (obj.hasOwnProperty(key)) {
                        newObject[key] = obj[key]
                    }
                }
                return newObject
            }

hasOwnProperty() 方法,该方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性。所有继承了 Object 的对象都会继承到 hasOwnProperty() 方法。这个方法可以用来检测一个属性是否是对象的自身属性。

深拷贝

深拷贝对象的两个必要点:
1.切断引用(拷贝生成的对象跟原有的对象没有任何关系)
2.原对象中的所有属性要出现在拷贝的新对象中(下面拷贝的都是自身属性)

1.JSON.parse(JSON.stringify(o))实现深拷贝

         let obj1 = {
                a: 0,
                b: {
                    c: 0,
                },
            }
            let obj2 = JSON.parse(JSON.stringify(obj1))
            obj1.a = 1
            obj1.b.c = 1     //修改obj1引用类型的属性值
            console.log(obj1) // {a: 1, b: {c: 1}}
            console.log(obj2) // {a: 0, b: {c: 0}} obj2没有收到影响

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

注意点:

  • 拷贝的对象中如果有函数,undefined,symbol,当使用过JSON.stringify()进行处理之后,都会消失。

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

  • 无法拷贝对象的原型链;

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

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

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

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

2.手写实现深拷贝

使用for in来遍历传入数据的属性值,如果值是基本类型就直接复制,如果是引用类型就进行递归调用该函数

            let obj = {
                uname: 'zkp',
                age: {
                    b: {
                        c: 12,
                    },
                },
                studyArr: ['html5', 'css', 'js', 'vue','node.js'],
            }
            // deepClone
            function deepClone(obj) {
                if (!(obj instanceof Object)) return obj   //不是对象实例,直接返回传入的数据
                let target = Array.isArray(obj) ? [] : {}  //根据obj类型初始化结果变量
                for (let i in obj) {
                    if (obj.hasOwnProperty(i)) { // 判断是否是自身属性
                        //判断数据i的类型
                        if (typeof obj[i] === 'object') {
                            target[i] = deepClone(obj[i])
                        } else {
                            target[i] = obj[i]
                        }
                    }
                }
                return target
            }
            //验证:
            let obj1 = deepClone(obj)
            console.log(obj2)    
            // 结果:
           //   obj = { uname: 'zkp', age: {  b: {  c: 12,}},
          //             studyArr: ['html5', 'css', 'js', 'vue','node.js'], }
          
          
          // 修改obj中引用类型的值
           obj.studyArr.push('react')
           obj.age.b.c = 20
           console.log(obj);
            // 结果:
           //   obj = { uname: 'zkp', age: {  b: {  c: 20,}},
          //             studyArr: ['html5', 'css', 'js', 'vue','node.js','react'], }
           
           console.log(obj2);
            // 结果:
           //   obj = { uname: 'zkp', age: {  b: {  c: 12,}},
          //             studyArr: ['html5', 'css', 'js', 'vue','node.js'], }
            

注意点:

这种方法只能对普通引用类型的值做递归复制,对于 Date、RegExp、Function 等引用类型不能正确拷贝;