前端深拷贝与浅拷贝(附实现方法)

理解

都是对原本的对象进行一份复制,差异如下:

  • 浅拷贝
    创建一个新对象,如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 。这个内存地址指向同一个堆内存,如果其中一个对象改变了堆内存存放的值,那么所有的对象都会受到影响。

  • 深拷贝
    创建一个新对象,如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,则从堆内存中开辟一个新的区域存放该引用类型指向的堆内存中的值,且修改新对象的值不会影响原对象。

放两张我做的蹩脚图加深一下理解:
在这里插入图片描述
在这里插入图片描述

总而言之,浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

代码实现

浅拷贝实现方法

  1. 展开运算符…
    ES6语法,可以非常方便的实现浅拷贝。
const obj1 = {
    
    
      name: "icy",
      age: 20,
      hobbies: ["eat", "sleep", "game"],
    };
    const obj2 = {
    
     ...obj1 };
    console.log(obj2);

输出:
在这里插入图片描述
为了验证是浅拷贝,我们改变一下obj中数组的第一项的值,然后再输出ojb1:

const obj1 = {
    
    
      name: "icy",
      age: 20,
      hobbies: ["eat", "sleep", "game"],
    };
    const obj2 = {
    
     ...obj1 };
    //修改堆内存中的值
    obj2.hobbies[0] = "play";

    console.log("修改后obj2", obj2);
    console.log("修改后obj1", obj1);

输出结果:
在这里插入图片描述
obj1和obj2都受到了影响,验证了浅拷贝。

  1. Object.assign() 方法
    该方法用于合并对象,用法 object.assign(合并的对象,合并的对象…)
    支持传入多个对象,用逗号分隔,返回值为合并后的新对象
  const obj1 = {
    
    
      name: "icy",
      age: 20,
      hobbies: ["eat", "sleep", "game"],
    };
    //将拷贝对象与{}空对象合并
    const obj2 = Object.assign({
    
    }, obj1);
    console.log(obj2);
  1. 函数库lodash的_.clone方法
    lodash中文文档:https://www.lodashjs.com/
    需要node环境,下载npm包。
    全局安装:
    npm i -g npm
    安装到依赖中:
    npm i --save lodash
import _ from "lodash";  //导入ladash包

const obj1 = {
    
    
  name: "icy",
  age: 20,
  hobbies: ["eat", "sleep", "game"],
};
const obj2 = _.clone(obj1);
console.log(obj2);
console.log(obj1.hobbies === obj2.hobbies); //true

结果如下:
在这里插入图片描述

  1. 数组合并方法 concat()
    该方法用于数组合并,合并的数组.concat(被合并的数组…)
    参数可有多个,用逗号分隔,返回合并后的数组。
    原理:用原数组去合并一个空数组,返回合并后的数组。
const arr1 = [ 1,2,3,{
    
    name:'icy',gender:'美少女']
const arr2 = arr1.concat()
  1. 数组剪裁方法 slice()
    该方法用途很多,可对数组进行增删,剪裁操作。
    mdn上slice介绍:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
const arr1 = [ 1,2,3,{
    
    name:'icy',gender:'美少女']
const arr2 = arr1.slice() //返回剪裁后的数组,这里没有剪裁掉任何项,相当于返回原数组

深拷贝的实现方式

  1. JSON.parse(JSON.stringify())
    利用JSON.stringfy()将对象转为json格式的字符串,再利用JSON.parse()将json格式的字符串转为对象,转换后的对象是一个新对象。
 const obj1 = {
    
    
      name: "icy",
      age: 22,
      gender: "美少女",
      hobbies: ["eat", "sleep", "game"],
    };
    const obj2 = JSON.parse(JSON.stringify(obj1)); //深拷贝后的对象
    console.log(obj2);

输出:
在这里插入图片描述
验证一下深拷贝:
改变obj2的hobbies[0],看下对obj1是否有影响:

    obj2.hobbies[0] = "看动漫";
    console.log("obj1===", obj1);
    console.log("obj2===", obj2);

在这里插入图片描述
结论:没有影响,深拷贝成功。

  • 注意点:
    这种方法虽然简单方便,可以实现数组或对象的深拷贝。但不能处理函数正则,因为这两者基于JSON.stringify和JSON.parse处理后,得到的正则就不再是正则(变为空对象),得到的函数就不再是函数(变为null)了。
    比如:
 const obj1 = {
    
    
      name: "icy",
      age: 22,
      gender: "美少女",
      hobbies: ["eat", "sleep", "game"],
      //函数
      watchComic: () => {
    
    
        console.log("icy 我不做人啦");
      },
      //正则
      regx: /^icy{3}$/g,
    };
    const obj2 = JSON.parse(JSON.stringify(obj1)); 
    console.log("obj2===", obj2);

输出:
在这里插入图片描述

函数没了,正则变为空对象。

  1. 函数库lodash的_.cloneDeep方法
    lodash提供了cloneDeep方法实现深拷贝
import _ from "lodash";
const obj1 = {
    
    
  name: "icy",
  age: 20,
  hobbies: ["eat", "sleep", "game"],
};
const obj2 = _.clone(obj1); //浅拷贝
console.log(obj2);
console.log(obj1.hobbies === obj2.hobbies); //true

const obj3 = _.cloneDeep(obj1); //深拷贝
console.log("obj3==", obj3); //false
console.log(obj1.hobbies === obj3.hobbies);

输出结果对比:
在这里插入图片描述
3. jQuery.extend()方法
jQuery提供了extend方法可以实现深拷贝。

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

import $ from "jquery"
const obj1 = {
    
    
  name: "icy",
  age: 20,
  hobbies: ["eat", "sleep", "game"],
};
var obj2 = $.extend(true, {
    
    }, obj1);
  1. 手写递归方法

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

下面是大佬写的,考虑到了,Date,正则,函数,对象循环引用(自己引用自己)的一个深拷贝递归:

 function deepClone(obj, hash = new WeakMap()) {
    
    
      if (obj === null) return obj; // 如果是null或者undefined就不进行拷贝操作
      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);

      let cloneObj = new obj.constructor();
      // 找到的是所属类原型上的constructor,而原型上的 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: 1, address: {
    
     x: 100 } };
    obj.o = obj; // 对象存在循环引用的情况
    let d = deepClone(obj);
    obj.address.x = 200;
    console.log(d);

参考文章:https://segmentfault.com/a/1190000020255831
https://juejin.cn/post/6844904197595332622

猜你喜欢

转载自blog.csdn.net/qq_43682422/article/details/127393325