# 深拷贝与序列化：将对象转为字符串并还原

URL: https://caijiao.org/posts/20
Source: docs/posts/20.md
Description: 介绍如何在不同环境下深拷贝对象、序列化为字符串，并从字符串还原为对象。深入探讨为何需要使用不同的技术栈（如 'structuredClone'、'lodash' 和 'JSON'）来实现这一功能。

在 [JavaScript](/javascript/) 开发中，我们常常需要将 JavaScript 对象转换为字符串，并在需要时将其还原为原始对象。这通常涉及到两个核心操作：**深拷贝（deep clone）** 和 **序列化（serialization）**。

本文将介绍如何使用 `structuredClone`、[Lodash](/lodash/) 和 `JSON` 方法，在不同环境下实现对象的深拷贝、序列化为字符串，并从字符串恢复为对象的功能。

## 1. 介绍

### 为什么需要深拷贝与序列化？

在现代 JavaScript 应用中，我们需要经常操作对象数据，如将其存储、传输或进行一些计算。**深拷贝**保证了创建的新对象与原对象在内存中完全独立，避免修改其中一个对象时影响另一个。而**序列化**（即将对象转为字符串）通常用于将对象数据存储到本地或通过网络传输。常见的例子包括存储到浏览器的 `localStorage` 或通过 HTTP 请求发送数据。

在实现这两个操作时，不同的环境（如浏览器、Node.js 等）会有不同的要求，因此我们需要采取不同的方式来实现它们。

## 2. 实现代码

### 2.1 方法一：`deepCloneAndStringify` - 将对象深拷贝并序列化为字符串

```javascript
import { cloneDeep } from 'lodash-es';

// 将对象深拷贝并序列化为字符串
function deepCloneAndStringify(obj) {
  let str;

  if (typeof process !== 'undefined' && process.versions && process.versions.node) {
    // Node.js 环境，使用 structuredClone 和 JSON 方法
    try {
      const clone = structuredClone(obj);  // 深拷贝
      str = JSON.stringify(clone);  // 转为字符串
    } catch (e) {
      console.error('Error using structuredClone/JSON methods in Node.js:', e);
      return null;
    }
  } else if (typeof window !== 'undefined') {
    // 浏览器环境，使用 structuredClone 和 JSON 方法
    try {
      const clone = structuredClone(obj);  // 深拷贝
      str = JSON.stringify(clone);  // 转为字符串
    } catch (e) {
      console.error('Error using structuredClone/JSON methods in browser:', e);
      return null;
    }
  } else {
    // 其他环境，使用 lodash 深拷贝
    const clone = cloneDeep(obj);
    str = JSON.stringify(clone);  // 转为字符串
  }

  return str;
}
````

### 2.2 方法二：`parseClone` - 解析字符串并恢复对象

```javascript
// 解析字符串并恢复对象
function parseClone(str) {
  let restored;

  if (typeof process !== 'undefined' && process.versions && process.versions.node) {
    // Node.js 环境，使用 JSON.parse 方法还原
    try {
      restored = JSON.parse(str);  // 还原为对象
    } catch (e) {
      console.error('Error parsing JSON in Node.js:', e);
      return null;
    }
  } else if (typeof window !== 'undefined') {
    // 浏览器环境，使用 JSON.parse 方法还原
    try {
      restored = JSON.parse(str);  // 还原为对象
    } catch (e) {
      console.error('Error parsing JSON in browser:', e);
      return null;
    }
  } else {
    // 其他环境，使用 lodash 深拷贝
    restored = cloneDeep(JSON.parse(str));  // 还原并深拷贝
  }

  return restored;
}
```

## 3. 示例

```javascript
const obj = { name: "John", age: 30 };

// 将对象转换为字符串
const str = deepCloneAndStringify(obj);
console.log('Stringified:', str);

// 将字符串还原为对象
const restoredObj = parseClone(str);
console.log('Restored:', restoredObj);
```

### 输出结果

```txt
Stringified: {"name":"John","age":30}
Restored: { name: 'John', age: 30 }
```

## 4. 代码解析

### 为什么要使用 `structuredClone` 和 `JSON`？

* **`structuredClone`**：这是浏览器和 Node.js 的现代 API，用于深拷贝对象。它能处理更复杂的数据类型，如 `Date`、`Map`、`Set` 等，并能避免 `JSON.stringify` 的一些限制。`structuredClone` 在大多数情况下是优先选择的方法，尤其在需要跨环境使用时非常有效。
* **`JSON.stringify` 和 `JSON.parse`**：这两个方法是序列化和反序列化对象的标准方法。它们非常适合简单对象的转换，但会丢失如函数、`undefined`、`RegExp` 等类型的信息。因此，对于这些类型的数据，`structuredClone` 更加合适。

### 为什么需要判断环境？

不同的运行环境（如浏览器和 Node.js）有不同的 API 支持和限制。比如：

* **浏览器环境**：可以直接使用 `structuredClone`，它是浏览器原生支持的 API，因此在浏览器中我们优先使用它。
* **Node.js 环境**：Node.js 从版本 17 开始支持 `structuredClone`，但如果需要兼容更早的版本，我们使用 `lodash` 来保证深拷贝功能。

## 5. 总结

通过这两个方法，开发者可以方便地将 JavaScript 对象序列化为字符串，并在需要时恢复成原始对象。根据不同的运行环境，代码选择不同的深拷贝和序列化实现，确保了功能的通用性和兼容性。

这种实现方式不仅保证了对象的独立性，还能在多种环境下稳定运行，解决了不同平台间的兼容性问题。希望这篇文章能帮助你更好地理解和实现对象的深拷贝与序列化功能！

> 相关文章：[JavaScript 序列化与反序列化：方法、性能与最优方案](/posts/19)
