JavaScript深拷贝的一些踩坑记录

yizhihongxing

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日

相关文章

  • js生成随机数的方法实例

    针对“js生成随机数的方法实例”,我将给出一份 Markdown 格式的完整攻略,包含以下内容: JS生成随机数的方法实例 需求描述 在 JavaScript 中,有时候需要生成一个随机数,比如在游戏开发中,需要随机生成一个游戏道具的数量,或者在网页上点击按钮后,需要随机显示某个图片,等等。因此,了解如何在 JavaScript 中生成随机数是很有必要的。 …

    JavaScript 2023年6月10日
    00
  • JS 中Json字符串+Cookie+localstorage

    以下是对于“JS中JSON字符串+Cookie+localStorage”的完整攻略: 1. 什么是JSON字符串? JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。它基于JavaScript语言的子集,由Douglas Crockford在2001年首次提出。JSON字符串是指符合JSON格式规范的字符串。 JS…

    JavaScript 2023年5月27日
    00
  • nodejs中实现阻塞实例

    实现阻塞实例需要用到Node.js中的核心模块fs和util。其中,fs模块用于读取文件内容,util模块中的promisify方法用于将回调函数转化为返回Promise对象的函数。 下面是一个完整的实现阻塞实例的攻略,包含两条示例说明: 1. 读取文件并输出内容 1.1 创建文件 首先,需要创建一个文件example.txt,并向其中写入一些内容。 ech…

    JavaScript 2023年5月28日
    00
  • javascript Function函数理解与实战

    JavaScript Function 函数理解与实战 1. 概述 函数是 JavaScript 中的重要概念之一。它是一块代码,用于完成特定的任务。函数通常有一个名称,可以提高代码的重用率,使代码更易于维护。在 JavaScript 中,函数也是第一类对象,也就是说,它们可以作为参数传递,返回值以及保存到变量中。 2. 基本语法 2.1 函数声明 函数通常…

    JavaScript 2023年5月27日
    00
  • 简单谈谈JS数组中的indexOf方法

    关于“简单谈谈JS数组中的indexOf方法”的攻略,我会详细讲解如下。 什么是 indexOf 方法 在 JavaScript 中,数组是一种常用的数据结构,它提供了许多方法来操作数组。其中一个非常有用的方法是 indexOf。这个方法可以用来查找数组中某个元素的位置。 如何使用 indexOf 方法 语法 arr.indexOf(searchElemen…

    JavaScript 2023年5月27日
    00
  • JS中‘hello’与new String(‘hello’)引出的问题详解

    当我们在JS中定义一个字符串时,我们可以使用字符串字面量 (string literal) 或者使用 String 对象 (String object)。 例如: let strLiteral = ‘hello’; let strObject = new String(‘hello’); 从上面的代码中可以看出,两种方式都可以定义一个字符串并将其赋值给变量。…

    JavaScript 2023年5月28日
    00
  • 一个简易时钟效果js实现代码

    下面我将为您详细讲解实现一个简易时钟效果的JavaScript代码。 实现步骤 1. HTML代码 首先,在页面中需要有一个DOM元素用来显示时钟,如下所示: <div id="clock"></div> 2. CSS代码 通过CSS样式调整时钟的外观,如下所示: #clock { width: 150px; he…

    JavaScript 2023年5月27日
    00
  • 微信小程序-详解微信登陆、微信支付、模板消息

    微信小程序-详解微信登陆、微信支付、模板消息 本攻略将详细介绍微信小程序中微信登陆、微信支付、模板消息的使用方法。 微信登陆 微信登陆可用于用户授权登陆、获取用户信息。 1. 微信开放平台配置 在微信开放平台中,配置小程序的“登陆授权”和“网页授权”,并获取小程序appid、appsecret。 2. 小程序配置 在小程序中,使用wx.login获取临时登录…

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