JS学习计划:深浅拷贝的那些事

683 阅读4分钟

前言

自己迷茫了一段时间,技术很low,想想现在勉强算年轻,必须要努力一番。所以记录下这个过程。只是一个记录自己学习的过程。
感谢掘金上的各位大神,跟着你们一路混分。

浅拷贝

  • 浅拷贝的属性如果是基本数据类型,拷贝的是这个基本数据类型的值;如果属性的引用类型,拷贝的是内存地址,所以如果一个对象改变了这个地址,就会影响到另外一个对象。

1.Object.asign

  • Object.assign()把所有可枚举类型从一个或者多个对象复制到目标对象,返回目标对象。缺点:Object.assign的拷贝属性市有限制的,只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举的属性。

const obj1={a:1,b:2  }

const obj2={a:1,b:[2,3,4,5],c:2};
Object.assign(obj1,obj2);

console.log(obj1);  //obj1={ a: 1, b: [ 2, 3, 4, 5 ], c: 2 }

  • Object.assign的使用场景

    • 便捷的为一个类添加属性和方法
    • 克隆对象,合并对象
    • 为属性指定默认值(此处注意默认对象的属性值最好都是简单类型,不然会发生一些很错误覆盖的问题)
//Object.assign的使用场景
class Person {
    constructor(x, y) {
      Object.assign(this, {x, y});
    }
}


Object.assign(Point.prototype, {
    eatFood() {
      console.log('hahah');
    },
    drinkWater() {
      //···
    }
  });
//为属性指定默认值
const default={url:"http://xxxx.com",port:8888}

Object.assgin(obj1,default,obj2) 

  • 使用Object.assign()的坑

    • 如果两个对象有相同的属性,则会覆盖。
    • 不是对象会先转成对象。null和undefined在首参数上就会报错。
    • 被拷贝的属性的更改会影响到拷贝之后的内容
    //使用Object.assign需要注意的几个点:
    const obj1={a:1,b:2  }

    const obj2={a:1,b:[2,3,4,5],c:2};
    Object.assign(obj1,obj2);

    console.log(obj1);  //obj1={ a: 1, b: [ 2, 3, 4, 5 ], c: 2 }
    //2.不是对象会先转成对象。
    Object.assign({},1); 
    Object.assign(undefined) // 报错
    Object.assign(null) // 报错
    
    let obj = {a: 1};
    Object.assign(obj, undefined) === obj // true
    Object.assign(obj, null) === obj // true

    //3.被拷贝的属性的更改会影响到拷贝之后的内容(浅拷贝)
    let  obj1={a:{b:2}};
    obj2={};
    Object.assign(obj2,obj1)
    obj1.a.b=3;
    console.log(obj2.a.b)  //3
    

...obj展开运算符

  • 展开运算符是 ES6 中新提出来的一种运算符。在拷贝数组、对象以及拼接数组等方面都可以使用。
    /**
 * @name ...obj拷贝对象
 */
const obj1 = {
    name: 'Mrking',
    arr1: ['1', '2', '3'],
    obj: {
      name: 'fighter',
      arr2: {b:{c:[1,2,3]}},
    },
  };
  const obj2 = {...obj1};
 
  console.log(obj1);
//   {
//     name: 'Mrking',
//     arr1: [ '1', '2', '3' ],
//     obj: { name: 'fighter', arr2: { b: [Object] } }
//   }
  console.log(obj2);
//   {
//     name: 'Mrking',
//     arr1: [ '1', '2', '3' ],
//     obj: { name: 'fighter', arr2: { b: [Object] } }
//   }

手写一个浅拷贝

 const shallowClone = (arr) => {
    const cloneArray = [];
    for (let prop in arr) {
        if (arr.hasOwnProperty(prop)) {
            cloneArray[prop] = arr[prop];
         }
     }
    return dst;
    }
    
const arr2 = shallowClone(arr1);

  • 若是修改引用类型的值,还是会改变拷贝后的对象。修改基本数据类型,则没有影响。

深拷贝

  • 深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存,当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相较于浅拷贝速度慢并且花销更大。深拷贝的两个对象互不影响。

JSON.parse(JSON.stringify(obj))

    //深拷贝对象或者是数组
    var a = {
    type: 'dog',
    desc: {
        name: 'wangwang',
        price: [300,500,1000]
        }
    };
    var b = JSON.parse(JSON.stringify(a));
    b.type = 'cat';
    b.desc.price[2] = 600;
    console.log(a); // { type: 'dog', desc: { name: 'wangwang', price: [ 300, 500, 1000 ] } }
    console.log(b); // { type: 'dog', desc: { name: 'wangwang', price: [ 300, 500, 600 ] } }

局限性

说到JSON.parse(JSON.stringify(obj)),咱们直接上一段代码即可。
    var a = {
    type: 'dog',
    desc: {
        name: 'wangwang',
        price: [300,500,1000]
    },
    sound:function(){
        console.log('wangwangwang')
    },
    banType:{
        a:undefined,
        b:new Date(),
        c:/[a-z]/,
        d:NaN,    
    }
    
};
var b = JSON.parse(JSON.stringify(a));
console.log(a);  
// {
//     type: 'dog',
//     desc: { name: 'wangwang', price: [ 300, 500, 1000 ] },
//     sound: [Function: sound],
//     banType: { a: undefined, b: 2020-05-03T03:20:21.019Z, c: /[a-z]/, d: NaN }
// }
console.log(b); 
// {
//     type: 'dog',
//     desc: { name: 'wangwang', price: [ 300, 500, 1000 ] },
//     banType: { b: '2020-05-03T03:20:21.019Z', c: {}, d: null }
// }

归纳:

  • 不能存放函数或者 Undefined,否则会丢失函数或者 Undefined。
  • 不要存放时间对象,否则会变成字符串形式。
  • 不能存放 RegExp对象,否则会变成空对象。
  • 不能存放NaN,会变成null。

手写一个简单版本深拷贝

  • 深拷贝的重点也就是在于如何把引用类型拷贝过来。
    function simpleDeepClone(a) {
        const b=Array.isArray(a) ? [] : {}
        for (const key of Object.keys(a)) {
        const type = typeof a[key]
        if (type !== 'object' || a[key] === null) {
        b[key] = a[key]
    } else {
        b[key] = simpleDeepClone(a[key])
    }
  }
    return b
}

最后

私下需要完善的地方:

  • 完善深拷贝的手写方法
  • 了解lodash函数库的cloneDeep()方法
  • 框架jQuery的extend()方法