js【详解】深拷贝 (含 JSON.parse(JSON.stringify(obj)) 的缺陷,5种手写深拷贝)

什么是深拷贝?

对于引用类型的数据,才有深浅拷贝的说法

  • 浅拷贝 :执行拷贝的变量只复制被拷贝变量内存的引用数据的地址。
被拷贝变量内地址指向的数据发生变化时,执行拷贝的变量也会同步改变
  • 深拷贝
  1. 在堆内存中开辟一个全新的存储空间
  2. 将被拷贝变量中引用地址对应的内容完整复制一份存入新的存储空间
  3. 将新的存储空间的地址存入执行拷贝的变量中。
被拷贝变量内地址指向的数据发生变化时,执行拷贝的变量不会改变

注意事项

  • Object.assign 不是深拷贝
// 只对 obj 的第一层进行了深拷贝,更深的层次依然是浅拷贝
const obj1 = Object.assign({}, obj, { s: 30 });

深拷贝方案一 JSON.parse(JSON.stringify(obj))

缺陷:

  • 属性值为函数和undefined的属性会丢失
  • 属性值为正则表达式的会变成{}
  • 属性值为时间对象的会变成时间字符串
let obj = {
    string: "字符串",
    Number: 10,
    null: null,
    undefined: undefined,
    date: new Date(),
    function: () => {
        console.log("我是一个函数");
    },
    RegExp: /^([0]{2}|0[1-9]|[1-9])\d*$/,
};

console.log("被拷贝的对象");
console.log(obj);
let objJSONclone = JSON.parse(JSON.stringify(obj));
console.log("使用JSON.parse(JSON.stringify(obj))拷贝对象");
console.log(objJSONclone);

运行结果

被拷贝的对象
{
  string: '字符串',    
  Number: 10,
  null: null,
  undefined: undefined,
  date: 2021-08-12T01:56:37.328Z,
  function: [Function: function],
  RegExp: /^([0]{2}|0[1-9]|[1-9])\d*$/
}
使用JSON.parse(JSON.stringify(obj))拷贝对象
{
  string: '字符串',
  Number: 10,
  null: null,
  date: '2021-08-12T01:56:37.328Z',
  RegExp: {}
}
  1. 只能克隆原始对象自身的值,不能克隆它继承的值
function Person (name) {
    this.name = name
}
var wanger = new Person('王二')
var newwanger = clone(wanger)
wanger.constructor === Person // true
newwanger.constructor === Object // true

深拷贝方案二【推荐】自定义函数cloneObj

参考代码1 【适合面试中手写深拷贝】

/**
 * 深拷贝
 * @param {Object} obj 要拷贝的对象
 */
function deepClone(obj = {}) {
    if (typeof obj !== 'object' || obj == null) {
        // obj 是 null ,或者不是对象和数组,直接返回
        return obj
    }

    // 初始化返回结果
    let result
    if (obj instanceof Array) {
        result = []
    } else {
        result = {}
    }

    for (let key in obj) {
        // 保证 key 不是原型的属性
        if (obj.hasOwnProperty(key)) {
            // 递归调用!!!
            result[key] = deepClone(obj[key])
        }
    }

    // 返回结果
    return result
}

参考代码2

function cloneObj(obj) {
    if (obj === null) return null;
    if (typeof obj !== "object") return obj;
    if (obj.constructor === Date) return new Date(obj);
    if (obj.constructor === RegExp) return new RegExp(obj);
    var newObj = new obj.constructor(); //保持继承链
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            //不遍历其原型链上的属性
            var val = obj[key];
            newObj[key] = typeof val === "object" ? arguments.callee(val) : val; // 使用arguments.callee解除与函数名的耦合
        }
    }
    return newObj;
}

使用方法

let objCloned = cloneObj(obj);

参考代码3

let deepClone = function (obj) {
    let copy = Object.create(Object.getPrototypeOf(obj));
    let propNames = Object.getOwnPropertyNames(obj);
    propNames.forEach(function (items) {
        let item = Object.getOwnPropertyDescriptor(obj, items);
        Object.defineProperty(copy, items, item);
 
    });
    return copy;
};

参考代码4

    function deepClone1(obj) {
      //判断拷贝的要进行深拷贝的是数组还是对象,是数组的话进行数组拷贝,对象的话进行对象拷贝
      var objClone = Array.isArray(obj) ? [] : {};
      //进行深拷贝的不能为空,并且是对象或者是
      if (obj && typeof obj === "object") {
        for (key in obj) {
          if (obj.hasOwnProperty(key)) {
            if (obj[key] && typeof obj[key] === "object") {
              objClone[key] = deepClone1(obj[key]);
            } else {
              objClone[key] = obj[key];
            }
          }
        }
      }
      return objClone;
    }

参考代码5

function deepClone(obj) {
    let result = typeof  obj.splice === "function" ? [] : {};
    if (obj && typeof obj === 'object') {
        for (let key in obj) {
            if (obj[key] && typeof obj[key] === 'object') {
                result[key] = deepClone(obj[key]);//如果对象的属性值为object的时候,递归调用deepClone,即在吧某个值对象复制一份到新的对象的对应值中。
            } else {
                result[key] = obj[key];//如果对象的属性值不为object的时候,直接复制参数对象的每一个键值到新的对象对应的键值对中。
            }
 
        }
        return result;
    }
    return obj;
}

深拷贝方案三 jQuery

 <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js"></script>

let array = [1,2,3,4];
let newArray = $.extend(true,[],array);
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

朝阳39

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值