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

简介: 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);
目录
相关文章
|
27天前
|
JSON 前端开发 JavaScript
聊聊 Go 语言中的 JSON 序列化与 js 前端交互类型失真问题
在Web开发中,后端与前端的数据交换常使用JSON格式,但JavaScript的数字类型仅能安全处理-2^53到2^53间的整数,超出此范围会导致精度丢失。本文通过Go语言的`encoding/json`包,介绍如何通过将大整数以字符串形式序列化和反序列化,有效解决这一问题,确保前后端数据交换的准确性。
33 4
|
2月前
|
存储 JSON JavaScript
JavaScript JSON
【10月更文挑战第7天】JSON 是 JavaScript 中非常重要的一个数据格式,它为数据的表示和传输提供了一种简单而有效的方式。掌握 JSON 的使用方法和特点,对于开发高质量的 JavaScript 应用具有重要意义。
|
3月前
|
存储 JSON JavaScript
js中JSON的使用
介绍JSON的基本概念和在JavaScript中的使用方式,包括JSON格式的语法规则、使用`JSON.stringify()`和`JSON.parse()`方法进行对象与字符串的转换,以及处理JSON数组数据。
js中JSON的使用
|
2月前
|
JavaScript 前端开发
JavaScript中的深拷贝与浅拷贝
JavaScript中的深拷贝与浅拷贝
51 4
|
2月前
|
JSON JavaScript 前端开发
JSON.parse()和JSON.stringify()用法
JSON.parse()和JSON.stringify()用法
64 1
|
2月前
|
JSON JavaScript 前端开发
js如何格式化一个JSON对象?
js如何格式化一个JSON对象?
100 3
|
3月前
|
XML JSON JavaScript
js的json格式
js的json格式
|
3月前
|
JSON JavaScript 数据格式
手写JS实现深拷贝函数
本文介绍了如何实现一个深拷贝函数`deepClone`,该函数可以处理对象和数组的深拷贝,确保拷贝后的对象与原始对象在内存中互不干扰。通过递归处理对象的键值对和数组的元素,实现了深度复制,同时保留了函数类型的值和基础类型的值。
23 3
|
3月前
|
存储 JSON JavaScript
JavaScript JSON
JavaScript JSON
36 5
|
2月前
|
机器学习/深度学习 JSON JavaScript
LangChain-21 Text Splitters 内容切分器 支持多种格式 HTML JSON md Code(JS/Py/TS/etc) 进行切分并输出 方便将数据进行结构化后检索
LangChain-21 Text Splitters 内容切分器 支持多种格式 HTML JSON md Code(JS/Py/TS/etc) 进行切分并输出 方便将数据进行结构化后检索
36 0