概念

浅拷贝:拷贝的只是对象的(引用)地址,新旧对象还是共享同一块内存,修改新对象会影响到原始对象数值发生变化
深拷贝:完全复制一个对象出来,新旧对象不共享内存,修改新对象不会改变原对象

浅拷贝

直接赋值

  • 任何操作都会影响原数组

    1. let arr = [1, 2, 3, 4]
    2. let newArr = arr
    3. newArr.push(5, 6, 7)
    4. console.log('newArr:', newArr) // [1, 2, 3, 4, 5, 6, 7]
    5. console.log('arr:', arr) // [1, 2, 3, 4, 5, 6, 7]

    Object.assign()

  • 拷贝属性值,假如属性值是一个对象的引用,那么也会指向那个引用

    1. let obj = {
    2. id: 1,
    3. name: 'karim',
    4. msg: {
    5. age: 18
    6. }
    7. };
    8. let newObj = {};
    9. Object.assign(newObj, obj);
    10. // 修改拷贝后的对象中某一个值后,原来的对象也会改变
    11. newObj.msg.age = 20;
    12. console.log('拷贝后新对象:', newObj.msg.age) // 拷贝后新对象: 20
    13. console.log('原对象:', obj.msg.age) // 原对象: 20

    Array.prototype.concat()

    注意:如果数组中都是简单数据类型,可以作为深拷贝使用

    1. let arr = [1, 2, 3, 4, 5]
    2. let newArr = [].concat(...arr)
    3. newArr.push(6)
    4. console.log('newArr', newArr) // [1, 2, 3, 4, 5, 6]
    5. console.log('arr', arr) // [1, 2, 3, 4, 5]

    实现浅拷贝

    1. let arr = [1, 2, 3, { obj: 'karim' }]
    2. let newArr = [].concat(...arr)
    3. console.log('newArr', newArr[3]) // {obj: "xiaoming"}
    4. newArr[3].obj = 'xiaoming'
    5. console.log('arr', arr[3]) // {obj: "xiaoming"}

    Array.prototype.slice()

    concat()方法同理,简单类型可以作为深拷贝

    1. let arr = [1, 2, 3, 4, 5]
    2. let newArr = arr.slice(0, 5)
    3. console.log('newArr', newArr) // [1, 2, 3, 4, 5]
    4. newArr.push(7)
    5. console.log('arr', arr) // [1, 2, 3, 4, 5, 7]

    实现浅拷贝

    1. let arr = [{ a: '123' }, { b: '456' }]
    2. let newArr = arr.slice(0, 2)
    3. console.log('newArr', newArr) // 0: {a: "123"} 1: {b: "789"}
    4. newArr[1].b = '789'
    5. console.log('arr', arr) // 0: {a: "123"} 1: {b: "789"}

    使用扩展运算符

    1. let arr = [{ a: '123' }, { b: '456' }]
    2. let newArr = [...arr]
    3. newArr[0].a = '789'
    4. console.log('newArr', newArr[0].a) // newArr 789
    5. console.log('arr', arr[0].a) // arr 789

    通过for..in循环进行对象的遍历

    ```javascript let obj = { id: 1, name: ‘karim’, msg: {

    1. age: 18

    } };

let newObj = {}; for (let k in obj) { / console.log(‘对象的值为:’, obj[k])/ newObj[k] = obj[k]; } // 修改拷贝后的对象中某一个值后,原来的对象也会改变 newObj.msg.age = 20 console.log(‘拷贝后新对象:’, newObj.msg.age); // 拷贝后新对象: 20 console.log(‘原对象:’, obj.msg.age) // 原对象 20

  1. 实现浅拷贝的方式不仅局限于上面的几种还可以用jQuery中的`$.extend()`函数,`Lodash`工具库的`clone`方法,都可以实现,可以自行`To learn on their own`下。
  2. <a name="510a44e6"></a>
  3. ## 深拷贝
  4. <a name="Es0o6"></a>
  5. ### 手动复制对象
  6. - 完全的`copy`一个新对象出来
  7. ```javascript
  8. let obj = {
  9. a: '123',
  10. b: '234'
  11. }
  12. let newObj = {
  13. a: obj.a,
  14. b: obj.b
  15. }
  16. newObj.a = '456'
  17. console.log('newObj', newObj)// {a: "456", b: "234"}
  18. console.log('obj', obj) // {a: "123", b: "234"}

使用JSON.parse(JSON.stringify(obj))

用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象,这个拷贝需要注意的是不能拷贝函数,因为JSON格式字符串不支持Function,在序列化的时候会自动删除

  1. let obj = {
  2. a: '123',
  3. b: '234'
  4. }
  5. let newObj = JSON.parse(JSON.stringify(obj))
  6. console.log(newObj) // {a: "123", b: "234"}
  7. newObj.a = '456'
  8. console.log(obj) // {a: "123", b: "234"}

使用递归的方式

  1. let obj = {
  2. id: 1,
  3. name: 'karim',
  4. msg: {
  5. age: 18
  6. },
  7. habits: ['reading', 'coding', 'playGames']
  8. };
  9. let newObj = {};
  10. function deepCopy(newObj, obj) {
  11. // 遍历对象
  12. for (let k in obj) {
  13. // 将属性值赋值到item变量中
  14. let item = obj[k];
  15. // 判断值是否是数组
  16. if (item instanceof Array) {
  17. newObj[k] = [];
  18. deepCopy(newObj[k], item);
  19. } else if (item instanceof Object) { // 判断是否是对象
  20. newObj[k] = {};
  21. deepCopy(newObj[k], item)
  22. } else { // 是否是简单数据类型
  23. newObj[k] = item;
  24. }
  25. }
  26. // 返回深拷贝后的对象
  27. return newObj
  28. }
  29. deepCopy(newObj, obj);
  30. newObj.msg.age = 21;
  31. console.log(newObj.msg.age); // 21
  32. console.log(obj.msg.age); // 18

其他方法大家可以自行摸索下,比如lodash_.cloneDeep()等等。
参考资料:
js深拷贝和浅拷贝及其实现方式
深拷贝的实现方式
如何写出一个惊艳面试官的深拷贝?