【JavaScript】深浅拷贝总结

145 阅读3分钟

赋值,浅拷贝,深拷贝三者区别

赋值

直接赋值对象的地址,因此两个对象指向同一个内存地址,会相互影响

    var a = [1, 2, 3, 4];
    var b = a;
    b.push(5);
    console.log(b); // 1,2,3,4,5
    console.log(a); // 1,2,3,4,5

拷贝

创建一个新对象,这个对象有着原始对象属性值的一份拷贝,如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是引用的内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象(它只能拷贝一层对象。如果有对象的嵌套,那么浅拷贝将无能为力

特点:新对象属性改变,不会影响基本类型的属性,但会影响引用类型的属性

深拷贝

将一个对象从内存中完整的拷贝一份出来,存放在一个新对象中

特点:对象指向不同地址,不会相互影响

图解---借助ConardLi大佬

QQ图片20210815174148.png

QQ图片20210815174226.png

深浅拷贝方法

浅拷贝方法

Object.assign()

Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。

    let obj1 = { person: { name: 'yyqx', age: 20 }, job: 'Star' };
    let obj2 = Object.assign({}, obj1);
    obj2.name = 'lyj';
    obj2.job = 'actor'
    console.log(obj1);

屏幕截图 2021-08-15 175106.png

展开运算符...

与Object.assign()的功能相同

    let obj1 = { name: 'yyqx', message: { job: 'Star', age: 20 } };
    let obj2 = { ...obj1 };
    obj1.name = 'lyj';
    obj1.message.job = 'actor'
    console.log(obj2);

屏幕截图 2021-08-15 175836.png

Array.prototype.concat()

    let arr1 = [11, 28, { name: 'yyqx' }];
    let arr2 = arr1.concat();
    arr2[2].name = 'lyj';
    console.log(arr1);

屏幕截图 2021-08-15 202443.png

Array.prototype.slice()

    let arr1 = [11, 28, { name: 'yyqx' }];
    let arr3 = arr1.slice();
    arr3[2].name = 'lyj';
    console.log(arr1);

屏幕截图 2021-08-15 202443.png

函数库lodash的_.clone方法

    var _ = require('lodash');
    var obj = {
      name: 'yyqx',
      message: { job: 'Star' },
      color: ['red', 'yellow', 'blue']
    }
    var obj1=_.clone(obj);
    console.log(obj1.message.job===obj1.message.job); // true

深拷贝方法

JSON.parse(JSON.stringify())

这也是利用JSON.stringify将对象转成JSON字符串,再用JSON.parse把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。

    let arr=[11,28,{name:'yyqx'}];
    let arr4=JSON.parse(JSON.stringify(arr));
    arr4[2].name='lyj';
    console.log(arr,arr4);

屏幕截图 2021-08-15 204408.png

这种方法虽然可以实现数组或对象深拷贝,但不能处理函数和正则,因为这两者基于JSON.stringify和JSON.parse处理后,得到的正则就不再是正则(变为空对象),得到的函数就不再是函数(变为null)了。

    let arr = [11, 28, { name: 'yyqx' }, function () { }];
    let arr4 = JSON.parse(JSON.stringify(arr));
    arr4[2].name = 'lyj';
    console.log(arr, arr4);

屏幕截图 2021-08-15 204834.png

JQuery.extend()方法

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

    var $ = require('jquery');
    var obj = {
    name: 'yyqx',
    message: { job: 'Star' },
    color: ['red', 'pink', 'blue']
    }
    var obj1 = $.extend(true, {}, obj);
    console.log(obj.message.job === obj1.message.obj);  // false

函数库loadsh的_.cloneDeep()方法

    var _ = require('loadsh');
    var obj = {
    name: 'yyqx',
    message: { job: 'Star' },
    color: ['red', 'pink', 'blue']
    }
    var obj1 = _.cloneDeep(obj);
    console.log(obj.message.job === obj1.message.obj);  // fasle

手写递归方法

递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝

详细版本---如何写出一个惊讶面试官的深拷贝

function deepClone(obj, hash = new WeakMap()) {
      // 如果是null或undefined,就不需要进行拷贝
      if (obj === null || obj === undefined) return obj;
      // 如果是内置对象,就需要返回新内置对象
      if (obj instanceof Date) return new Date(obj);
      if (obj instanceof RegExp) return new RegExp(obj);
      // 如果是函数或者基本数据类型,不需要深拷贝
      if (typeof obj !== 'object') return obj;
      // 是对象的话就要进行深拷贝
      if (hash.get(obj)) return hash.get(obj);
      // obj是对象Object的实例对象,obj中的__proto__指向Object.prototype,
      // 而Object.prototype中的constructor指向Object本身
      // 所以相当于新创建了一个对象的实例
      let cloneObj = new obj.constructor();
      hash.set(obj, cloneObj);
      for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
          // 实现一个递归拷贝
          cloneObj[key] = deepClone(obj[key], hash);
        }
      }
      return cloneObj;
    }
  • 实验
 let obj = { name: 'yyqx', add: { x: 100 } };
    obj.o = obj; // 对象存在循环引用的情况
    let obj1 = deepClone(obj);
    obj.add.x = 200;
    obj1.name = 'lyj'
    console.log(obj,obj1);

屏幕截图 2021-08-15 214952.png

总结

仅为学习笔记!!!