JavaScript深拷贝的一些踩坑记录

JavaScript深拷贝的一些踩坑记录

在 JavaScript 中,深拷贝是比较常见的操作,特别是在处理复杂的数据结构时。但是,JavaScript 中的深拷贝有很多坑点,如果处理不当,就会发生莫名其妙的错误。本文就来总结一下 JavaScript 深拷贝时常见的问题和解决方案。

为什么要进行深拷贝

在 JavaScript 中,对象是通过引用来传递的。这个引用实际上就是指向对象存储位置的指针。如果直接把一个对象赋值给另外一个变量,那么这两个变量就会指向同一个对象。这种情况在开发中很常见,比如一个函数返回了一个对象,然后被调用方修改了这个对象,那么这个修改会对其他地方的引用也生效,进而引发意外的结果。

为了避免这种情况,深拷贝(deep copy)就应运而生了。深拷贝可以创建一个新的对象,这个新对象和原来的对象完全独立,修改新对象不会对原有对象造成影响。

浅拷贝和深拷贝

在进行深拷贝之前,我们先了解一下浅拷贝和深拷贝的区别。

浅拷贝(shallow copy)只是拷贝了一层对象的属性,如果这个对象的属性里面还有对象或者数组,浅拷贝操作并不会将这些对象或者数组进行拷贝,而是共享同一份数据。

const obj = {
  name: '张三',
  age: 18,
  hobbies: ['reading', 'coding', 'sports']
};

const obj2 = Object.assign({}, obj);

console.log(obj.hobbies === obj2.hobbies); // true,obj2.hobbies 与 obj.hobbies 数组指向同一块内存区域

深拷贝(deep copy)则是递归的拷贝对象的属性和子属性,直到所有属性都是基本类型数据。

const obj = {
  name: '张三',
  age: 18,
  hobbies: ['reading', 'coding', 'sports']
};

// 深拷贝可以通过 JSON.parse(JSON.stringfy()) 实现
const obj2 = JSON.parse(JSON.stringify(obj));

console.log(obj.hobbies === obj2.hobbies); // false,obj2.hobbies 和 obj.hobbies 指向不同的内存区域

值得注意的是,JSON 对象有一些限制,不能序列化函数、正则表达式等特殊对象,而且会忽略 undefined 和 symbol 类型的属性,这些情况下则需要用其他方法来实现深拷贝。

踩坑记录

问题1:循环引用

循环引用指的是对象属性中存在指向对象本身的引用。如果在深拷贝时不做处理,就会出现循环引用导致死循环的问题。

const obj = {
  name: '张三'
};

obj.me = obj;

// 如果使用 JSON 方式进行深拷贝,会报错:TypeError: Converting circular structure to JSON
const obj2 = JSON.parse(JSON.stringify(obj));

// 如果使用递归方式实现深拷贝,也会陷入死循环
function deepClone(obj) {
  const newObj = {};

  for (let key in obj) {
    if (typeof obj[key] === 'object') {
      newObj[key] = deepClone(obj[key]);
    } else {
      newObj[key] = obj[key];
    }
  }

  return newObj;
}

const obj3 = deepClone(obj); // 报错:Uncaught RangeError: Maximum call stack size exceeded

解决方案:

需要在递归过程中记录已经被处理过的对象,如果下一次递归访问到了已经被处理过的对象,就直接使用之前处理过的结果。

function deepClone(obj, map = new WeakMap()) {
  if (map.has(obj)) {
    return map.get(obj);
  }

  const newObj = {};

  map.set(obj, newObj); // 记录已经处理的对象

  for (let key in obj) {
    if (typeof obj[key] === 'object') {
      newObj[key] = deepClone(obj[key], map);
    } else {
      newObj[key] = obj[key];
    }
  }

  return newObj;
}

const obj4 = deepClone(obj); // 正常返回,obj4.me === obj4

问题2:非标准 JSON 对象成员

JSON 对象有一些限制,比如不能序列化函数、正则表达式等类型数据。那么,如果对象中存在这些类型的属性,使用 JSON 方法进行深拷贝时就会出现问题。

const obj = {
  name: '李四',
  sayHello() {
    console.log('Hello!');
  },
  reg: /test/g
};

const obj2 = JSON.parse(JSON.stringify(obj)); // 报错:TypeError: Converting circular structure to JSON

解决方案:

针对这种情况,可以写一个自定义的序列化函数,在遍历对象属性时,对于函数、正则表达式等非标准对象成员进行特殊处理。

function deepClone(obj, map = new WeakMap()) {
  if (map.has(obj)) {
    return map.get(obj);
  }

  const newObj = {};

  map.set(obj, newObj);

  for (let key in obj) {
    if (typeof obj[key] === 'object' && obj[key] !== null) {
      newObj[key] = deepClone(obj[key], map);
    } else if (typeof obj[key] === 'function') {
      newObj[key] = `function ${obj[key].name}() { ... }`;
    } else if (obj[key] instanceof RegExp) {
      newObj[key] = obj[key].toString();
    } else {
      newObj[key] = obj[key];
    }
  }

  return newObj;
}

const obj3 = deepClone(obj);
console.log(obj3.reg); // /test/g

总结

JavaScript 中的深拷贝操作并不简单,需要特别注意循环引用、非标准对象成员等情况。在实际开发中,可以根据不同的需求选择不同的深拷贝方式,并且需要对深拷贝方式进行有效的测试和错误处理,确保深拷贝操作不会出现莫名其妙的错误。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:JavaScript深拷贝的一些踩坑记录 - Python技术站

(0)
上一篇 2023年6月10日
下一篇 2023年6月10日

相关文章

  • javascript 基础篇1 什么是js 建立第一个js程序

    我来分享一下 “JavaScript 基础篇1:什么是 JavaScript?建立第一个 JavaScript 程序” 的完整攻略。 什么是 JavaScript? JavaScript 是一种高级编程语言,用于创建交互式网页和网页应用程序。它是一种脚本语言,意味着你不需要为了编写代码而拥有庞大的开发环境。 JavaScript 非常流行,现在可以在几乎所有…

    JavaScript 2023年5月27日
    00
  • JavaScript字符串对象fromCharCode方法入门实例(用于把Unicode值转换为字符串)

    JavaScript字符串对象fromCharCode方法入门实例 简介 fromCharCode()是JavaScript中字符串对象的一个方法,用来将Unicode编码转换成实际字符串。 语法 String.fromCharCode(num1, num2, …, numN) 参数 num1 – 必选参数,当前要被转换的Unicode编码值 (必须在0…

    JavaScript 2023年5月19日
    00
  • JS作为值的函数用法示例

    JS作为值的函数用法示例即为将函数定义作为一个值来使用,可以将函数定义作为一个变量赋值给变量、集合或对象中的属性,也可以将函数作为一个参数传递给其他函数。下面是两个示例说明: 示例一:将函数作为参数传递给其他函数 // 定义一个函数 function sayHi(name) { console.log(‘Hi ‘ + name + ‘!’); } // 定义…

    JavaScript 2023年5月27日
    00
  • 小程序云开发初探(小结)

    小程序云开发初探(小结) 本文主要介绍小程序云开发的基础知识和使用方法。小程序云开发是微信小程序提供的一项新功能,可以通过云数据库、云存储和云函数来快速搭建一个完整的小程序。 1. 云开发环境配置 要使用小程序云开发,需要在微信公众平台上创建小程序,并在小程序后台开启云开发。 注册微信小程序账号 登录小程序后台,点击“设置”-“开发设置”,在云开发中开启开发…

    JavaScript 2023年6月10日
    00
  • javascript self对象使用详解

    JavaScript Self对象使用详解 什么是Self对象? Self对象指的是JavaScript中的this关键字,它代表当前对象。可以在对象的方法中使用this关键字来引用当前对象,或者用在一个方法中引用其他方法。 如何使用Self对象? 在对象方法中使用Self对象 在JavaScript的对象方法中使用this关键字可以引用到当前的对象。如下例…

    JavaScript 2023年5月27日
    00
  • js传值后台中文出现乱码的解决方法

    下面是详细讲解“js传值后台中文出现乱码的解决方法”的完整攻略: 问题描述 在前端页面使用 JavaScript 传递参数给后台时,中文参数会出现乱码! 根本原因 乱码的出现是因为前端传递参数时,使用了不同的字符集编码。而后台在解析字符时使用的编码集与前端传递的不同,就导致中文字符的解析出现了不一致的问题。 解决方法 下面介绍两条解决办法: 方法一:转码传递…

    JavaScript 2023年5月19日
    00
  • js动态设置div的值下例子

    下面让我来详细讲解“js动态设置div的值”的完整攻略。 基本概念 在开始具体的实现代码之前,我们先来了解一下这个问题的基本概念。 动态设置div的值:指的是通过JavaScript脚本代码实现在网页中的某个元素(如div)中动态设置内容,而不是直接在HTML代码中写死。 实现过程 实现动态设置div的值可以分为以下两个步骤: 1. 获取div元素 在Jav…

    JavaScript 2023年6月11日
    00
  • Textbox控件注册回车事件及触发按钮提交事件具体实现

    Textbox控件注册回车事件及触发按钮提交事件是Web开发中常用的技术之一。下面我将详细讲解如何实现这个功能。 注册回车事件 在Textbox控件中,我们可以向其绑定keypress事件,通过该事件判断当用户按下回车键时执行某些操作,比如提交表单等。下面是一个简单示例: <input type="text" id="my…

    JavaScript 2023年6月11日
    00
合作推广
合作推广
分享本页
返回顶部