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);
目录
相关文章
|
4天前
|
JavaScript 前端开发 CDN
前端 JS 经典:package.json 属性详解
前端 JS 经典:package.json 属性详解
9 1
|
13天前
|
Web App开发 JSON JavaScript
JavaScript对象常用操作JSON总结
JavaScript对象常用操作JSON总结
21 8
|
3天前
|
JSON JavaScript 前端开发
|
5天前
|
存储 JavaScript 前端开发
javascript的栈内存 VS 堆内存(浅拷贝 VS 深拷贝)
javascript的栈内存 VS 堆内存(浅拷贝 VS 深拷贝)
7 0
|
15天前
|
JavaScript 前端开发
JS中浅拷贝和深拷贝的区别
JS中浅拷贝和深拷贝的区别
11 0
|
1天前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的个人健康管理系统小程序附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的个人健康管理系统小程序附带文章源码部署视频讲解等
8 2
|
1天前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的高校宿舍信息管理系统小程序附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的高校宿舍信息管理系统小程序附带文章源码部署视频讲解等
7 1
|
1天前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的电影交流平台小程序附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的电影交流平台小程序附带文章源码部署视频讲解等
7 1
|
1天前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的电器维修系统小程序附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的电器维修系统小程序附带文章源码部署视频讲解等
7 1
|
1天前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的大学生校园兼职微信小程序附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的大学生校园兼职微信小程序附带文章源码部署视频讲解等
9 1