Skip to content

Commit

Permalink
zh-cn: resolve the global function structuredClone()
Browse files Browse the repository at this point in the history
  • Loading branch information
yin1999 committed Sep 27, 2024
1 parent 649e024 commit 1ad0926
Show file tree
Hide file tree
Showing 17 changed files with 111 additions and 48 deletions.
10 changes: 6 additions & 4 deletions files/zh-cn/glossary/deep_copy/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ let ingredients_list_deepcopy = JSON.parse(JSON.stringify(ingredients_list));
由于深拷贝与其源对象不共享引用,因此对深拷贝所做的任何更改都不会影响源对象。

```js
// 改变 ingredients_list_deepcopy 中 'list' 属性的值。
// 改变 ingredients_list_deepcopy 中list属性的值。
ingredients_list_deepcopy[1].list = ["rice flour", "water"];
// ingredients_list 的“list”属性不会发生变化。
console.log(ingredients_list[1].list);
Expand All @@ -49,9 +49,11 @@ console.log(ingredients_list[1].list);

然而,虽然上面代码中的对象足够简单,可以[序列化](/zh-CN/docs/Glossary/Serialization),但许多 JavaScript 对象根本不能序列化——例如,[函数](/zh-CN/docs/Web/JavaScript/Guide/Functions)(带有闭包)、[Symbol](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol)、在 [HTML DOM API](/zh-CN/docs/Web/API/HTML_DOM_API) 中表示 HTML 元素的对象、递归数据以及许多其他对象。在这种情况下,调用 `JSON.stringify()` 来序列化对象将会失败。所以没有办法对这些对象进行深拷贝。

你也可以使用 Web API [`structuredClone()`](/zh-CN/docs/Web/API/structuredClone) 来创建深拷贝。`structuredClone()` 的优点是允许源代码中的[可转移对象](/zh-CN/docs/Web/API/Web_Workers_API/Transferable_objects)*转移*到新的副本,而不仅仅是克隆。它还能处理更多的数据类型,如 `Error`。但是请注意,`structuredClone()` 不是 JavaScript 语言本身的特性——相反,它是浏览器和任何其他实现了 [`window`](/zh-CN/docs/Web/API/Window) 这样全局对象的 JavaScript 运行时的一个特性。调用 `structuredClone()` 来克隆一个不可序列化的对象,与调用 `JSON.stringify()` 来序列化一个不可序列化的对象一样,会失败。
你也可以使用 Web API {{DOMxRef("Window.structuredClone", "structuredClone()")}} 来创建深拷贝。`structuredClone()` 的优点是允许源代码中的[可转移对象](/zh-CN/docs/Web/API/Web_Workers_API/Transferable_objects)*转移*到新的副本,而不仅仅是克隆。它还能处理更多的数据类型,如 `Error`。但是请注意,`structuredClone()` 不是 JavaScript 语言本身的特性——相反,它是浏览器和任何其他实现了 [`window`](/zh-CN/docs/Web/API/Window) 这样全局对象的 JavaScript 运行时的一个特性。调用 `structuredClone()` 来克隆一个不可序列化的对象,与调用 `JSON.stringify()` 来序列化一个不可序列化的对象一样,会失败。

## 参见

- {{glossary("Shallow copy", "浅拷贝")}}
- [`window.structuredClone()`](/zh-CN/docs/Web/API/structuredClone)
- 相关术语:
- {{glossary("Shallow copy", "浅拷贝")}}
- {{DOMxRef("Window.structuredClone()")}}
- {{DOMxRef("WorkerGlobalScope.structuredClone()")}}
2 changes: 1 addition & 1 deletion files/zh-cn/glossary/serializable_object/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ slug: Glossary/Serializable_object

{{GlossarySidebar}}

**可序列化对象**(Serializable object)是可以在任何 JavaScript 环境(领域,realm)中序列化、反序列化的对象。例如,这允许将此类对象存储在磁盘上并在以后进行恢复,或使用 {{domxref("structuredClone()")}} 对它们进行拷贝,又或者使用 {{domxref("DedicatedWorkerGlobalScope.postMessage()")}} 在 Worker 之间共享它们。
**可序列化对象**(Serializable object)是可以在任何 JavaScript 环境(领域,realm)中序列化、反序列化的对象。例如,这允许将此类对象存储在磁盘上并在以后进行恢复,或使用 {{DOMxRef("Window.structuredClone", "structuredClone()")}} 对它们进行拷贝,又或者使用 {{domxref("DedicatedWorkerGlobalScope.postMessage()")}} 在 Worker 之间共享它们。

序列化可能不包括原始对象的所有属性和其他方面的内容。例如,对 {{domxref("DOMException")}} 的序列化必须包含 `name``message` 属性,但是否包含其他属性则取决于具体实现。因此,反序列化的对象并不一定是与原始对象相同的拷贝。但是,反序列化得到的新的对象将会是一个{{glossary("deep copy", "深拷贝")}},因此任何从原始对象序列化并反序列化到新的对象的属性都不会和原始对象共享任何引用。

Expand Down
2 changes: 1 addition & 1 deletion files/zh-cn/web/api/domexception/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ slug: Web/API/DOMException

每个异常都有一个**名称**(name),一个采用驼峰命名法的简短字符串,用于描述识别错误或异常情况。

`DOMException` 是一个{{Glossary("Serializable object","可序列化对象")}},因此可以使用 {{domxref("structuredClone()")}} 克隆,或使用 {{domxref("Worker.postMessage()", "postMessage()")}} 在 [Worker](/zh-CN/docs/Web/API/Worker) 之间复制。
`DOMException` 是一个{{Glossary("Serializable object","可序列化对象")}},因此可以使用 {{DOMxRef("Window.structuredClone", "structuredClone()")}} 克隆,或使用 {{domxref("Worker.postMessage()", "postMessage()")}} 在 [Worker](/zh-CN/docs/Web/API/Worker) 之间复制。

## 构造函数

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ slug: Web/API/Web_Workers_API/Functions_and_classes_available_to_workers
- {{domxref("reportError()")}}
- {{domxref("setInterval()")}}
- {{domxref("setTimeout()")}}
- {{domxref("structuredClone()")}}
- {{DOMxRef("WorkerGlobalScope.structuredClone", "structuredClone()")}}
- {{domxref("DedicatedWorkerGlobalScope.requestAnimationFrame()", "requestAnimationFrame()")}}(仅专用 worker)
- {{domxref("DedicatedWorkerGlobalScope.cancelAnimationFrame()", "cancelAnimationFrame()")}}(仅专用 worker)

Expand Down
2 changes: 1 addition & 1 deletion files/zh-cn/web/api/web_workers_api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ worker 在一个与当前 {{DOMxRef("window")}} 不同的全局上下文中运
- {{domxref("reportError()")}}
- {{domxref("setInterval()")}}
- {{domxref("setTimeout()")}}
- {{domxref("structuredClone()")}}
- {{DOMxRef("WorkerGlobalScope.structuredClone", "structuredClone()")}}
- {{domxref("DedicatedWorkerGlobalScope.requestAnimationFrame()", "requestAnimationFrame()")}}(仅专用 worker)
- {{domxref("DedicatedWorkerGlobalScope.cancelAnimationFrame()", "cancelAnimationFrame()")}}(仅专用 worker)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ slug: Web/API/Web_Workers_API/Structured_clone_algorithm

- [HTML 规范:安全地传递结构化数据](https://html.spec.whatwg.org/multipage/infrastructure.html#safe-passing-of-structured-data)
- [可转移对象](/zh-CN/docs/Web/API/Web_Workers_API/Transferable_objects)
- {{domxref("structuredClone()")}}
- {{DOMxRef("Window.structuredClone()")}}
- {{DOMxRef("WorkerGlobalScope.structuredClone()")}}
- {{domxref("window.history")}}
- {{domxref("window.postMessage()")}}
- [Web Worker](/zh-CN/docs/Web/API/Web_Workers_API)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ slug: Web/API/Web_Workers_API/Transferable_objects

*可转移对象*通常用于共享资源,该资源一次仅能安全地暴露在一个 JavaScript 线程中。例如,{{jsxref("ArrayBuffer")}} 是一个拥有内存块的可转移对象。当此类缓冲区(buffer)在线程之间传输时,相关联的内存资源将从原始的缓冲区分离出来,并且附加到新线程创建的缓冲区对象中。原始线程中的缓冲区对象不再可用,因为它不再拥有属于自己的内存资源了。

使用 {{domxref("structuredClone()")}} 创建对象的深层拷贝时,也可以使用转移。克隆操作后,传输的资源将被移动到克隆的对象,而不是复制。
使用 {{DOMxRef("WorkerGlobalScope.structuredClone", "structuredClone()")}} 创建对象的深层拷贝时,也可以使用转移。克隆操作后,传输的资源将被移动到克隆的对象,而不是复制。

使用转移对象资源的机制取决于对象自身。例如,当 {{jsxref("ArrayBuffer")}} 在线程之间转移时,它指向的内存资源*实际上*以快速且高效的零拷贝操作在上下文之间移动。其他对象可以通过拷贝关联的资源,然后将它从旧的上下文中删除来转移它。

Expand All @@ -34,7 +34,7 @@ console.log(uInt8Array.byteLength); // 0
### 在进行克隆操作时转移

以下代码展示了 {{domxref("structuredClone()")}} 操作,将底层缓冲区从原始对象复制到克隆对象(`clone`)。
以下代码展示了 `structuredClone()` 操作,将底层缓冲区从原始对象复制到克隆对象(`clone`)。

```js
const original = new Uint8Array(1024);
Expand Down
57 changes: 28 additions & 29 deletions files/zh-cn/web/api/window/structuredclone/index.md
Original file line number Diff line number Diff line change
@@ -1,81 +1,80 @@
---
title: structuredClone()
title: Window:structuredClone() 方法
slug: Web/API/Window/structuredClone
original_slug: Web/API/structuredClone
l10n:
sourceCommit: 8b6cec0ceff01e7a9d6865cf5306788e15cce4b8
---

{{APIRef("HTML DOM")}}{{AvailableInWorkers}}
{{APIRef("HTML DOM")}}

全局的 **`structuredClone()`** 方法使用[结构化克隆算法](/zh-CN/docs/Web/API/Web_Workers_API/Structured_clone_algorithm)将给定的值进行[深拷贝](/zh-CN/docs/Glossary/Deep_copy)
{{domxref("Window")}} 接口的 **`structuredClone()`** 方法使用[结构化克隆算法](/zh-CN/docs/Web/API/Web_Workers_API/Structured_clone_algorithm)将给定的值进行{{Glossary("deep copy", "深拷贝")}}

该方法还支持把原值中的[可转移对象](/zh-CN/docs/Web/API/Web_Workers_API/Transferable_objects)_转移_(而不是拷贝)到新对象上。可转移对象与原始对象分离并附加到新对象;它们将无法在原始对象中被访问。

## 语法

```js-nolint
structuredClone(value)
structuredClone(value, { transfer })
structuredClone(value, options)
```

### 参数

- `value`
- : 被克隆的对象。可以是任何[结构化克隆支持的类型](/zh-CN/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#支持的类型)
- `transfer` {{optional_inline}}
- : 是一个[可转移对象](/zh-CN/docs/Web/API/Web_Workers_API/Transferable_objects)的数组,里面的 `` 并没有被克隆,而是被转移到被拷贝对象上。
- `options` {{optional_inline}}

- : 一个具有以下属性的对象:

- `transfer`
- : 一个[可转移对象](/zh-CN/docs/Web/API/Web_Workers_API/Transferable_objects)的数组,里面的对象将被移动而不是克隆到返回的对象上。

### 返回值

返回值是原始``[深拷贝](/zh-CN/docs/Glossary/Deep_copy)
原始值(`value`[深拷贝](/zh-CN/docs/Glossary/Deep_copy)

### 异常

- `DataCloneError` {{domxref("DOMException")}}
- : 如果输入值的任一部分不可序列化,则抛出该异常。

## 附注
## 描述

这个函数可以用来进行[深拷贝](/zh-CN/docs/Glossary/Deep_copy) JavaScript 变量。
也支持循环引用,如下所示:
这个函数可以用来进行[深拷贝](/zh-CN/docs/Glossary/Deep_copy) JavaScript 变量。也支持循环引用,如下所示:

```js
// Create an object with a value and a circular reference to itself.
// 创建一个具有值和对自身的循环引用的对象。
const original = { name: "MDN" };
original.itself = original;

// Clone it
// 对它进行克隆
const clone = structuredClone(original);

console.assert(clone !== original); // the objects are not the same (not same identity)
console.assert(clone.name === "MDN"); // they do have the same values
console.assert(clone.itself === clone); // and the circular reference is preserved
console.assert(clone !== original); // 对象并不相同(标识不同)
console.assert(clone.name === "MDN"); // 它们具有相同的值
console.assert(clone.itself === clone); // 且保留了循环引用
```

### Transferring values
### 转移值

使用可选参数 `transfer` 里的值,可以使[可转移对象](/zh-CN/docs/Web/API/Web_Workers_API/Transferable_objects)(仅)被传递,不被克隆。
传输导致原始对象(里的属性)无法继续使用。
使用参数 `options` 的里 `transfer` 属性,可以使[可转移对象](/zh-CN/docs/Web/API/Web_Workers_API/Transferable_objects)(仅)被传递,不被克隆。转移会导致原始对象无法继续使用。

> [!NOTE]
> 一个可能有用的场景是在保存 buffer 之前先异步的校验里面的数据。为了避免 buffer 在保存之前有其他修改,你可以先克隆这个 buffer 然后校验数据。为了防止意外的错误引用,在传输数据时,任何修改 buffer 的尝试都会失败
> 一个可能有用的场景是在保存缓冲区之前先异步的校验里面的数据。为了避免缓冲区在保存之前有其他修改,你可以先克隆这个缓冲区然后校验数据。为了防止意外的错误引用,在传输数据时,任何修改缓冲区的尝试都会失败
以下演示了如何把一个数组的属性转义到新对象。
返回结果时,原始对象里的 `uInt8Array.buffer` 会被清除掉。
以下演示了如何把一个数组的属性转移到新对象。返回结果时,原始对象里的 `uInt8Array.buffer` 会被清除掉。

```js
var uInt8Array = new Uint8Array(1024 * 1024 * 16); // 16MB
for (var i = 0; i < uInt8Array.length; ++i) {
uInt8Array[i] = i;
}
// 16MB = 1024 * 1024 * 16
const uInt8Array = Uint8Array.from({ length: 1024 * 1024 * 16 }, (v, i) => i);

const transferred = structuredClone(uInt8Array, {
transfer: [uInt8Array.buffer],
});
console.log(uInt8Array.byteLength); // 0
```

你可以克隆任意数量的对象,并传输对象的任意子集。
例如 以下代码会把 `arrayBuffer1` 作为值传输 但不是 `arrayBuffer2`
你可以克隆任意数量的对象,并转移对象的任意子集。例如,以下代码会把 `arrayBuffer1` 作为值转移(而不是 `arrayBuffer2`)。

```js
const transferred = structuredClone(
Expand Down Expand Up @@ -106,7 +105,7 @@ console.log(mushrooms1.amanita); // ["muscaria"]

### 转移一个对象

在本示例中我们创建了一个 {{jsxref("ArrayBuffer")}} 然后克隆将它作为属性的对象,将它转移。我们可以使用克隆对象里的 buffer,但是如果我们尝试使用原对象的 buffer 的话就会产生异常
在本示例中我们创建了一个 {{jsxref("ArrayBuffer")}} 然后克隆将它作为属性的对象,将它转移。我们可以使用克隆对象里的缓冲区(buffer),但是如果我们尝试使用原对象的缓冲区的话就会产生异常

```js
// 创建一个给定字节大小的 ArrayBuffer
Expand Down
61 changes: 61 additions & 0 deletions files/zh-cn/web/api/workerglobalscope/structuredclone/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
title: WorkerGlobalScope:structuredClone() 方法
slug: Web/API/WorkerGlobalScope/structuredClone
l10n:
sourceCommit: 8b6cec0ceff01e7a9d6865cf5306788e15cce4b8
---

{{APIRef("Web Workers API")}}{{AvailableInWorkers("worker")}}

{{domxref("WorkerGlobalScope")}} 接口的 **`structuredClone()`** 方法使用[结构化克隆算法](/zh-CN/docs/Web/API/Web_Workers_API/Structured_clone_algorithm)将给定的值进行{{Glossary("deep copy", "深拷贝")}}。

该方法还支持把原值中的[可转移对象](/zh-CN/docs/Web/API/Web_Workers_API/Transferable_objects)_转移_(而不是拷贝)到新对象上。可转移对象与原始对象分离并附加到新对象;它们将无法在原始对象中被访问。

## 语法

```js-nolint
structuredClone(value)
structuredClone(value, options)
```

### 参数

- `value`
- : 被克隆的对象。可以是任何[结构化克隆支持的类型](/zh-CN/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#支持的类型)
- `options` {{optional_inline}}

- : 一个具有以下属性的对象:

- `transfer`
- : 一个[可转移对象](/zh-CN/docs/Web/API/Web_Workers_API/Transferable_objects)的数组,里面的对象将被移动而不是克隆到返回的对象上。

### 返回值

原始值(`value`)的[深拷贝](/zh-CN/docs/Glossary/Deep_copy)

### 异常

- `DataCloneError` {{domxref("DOMException")}}
- : 如果输入值的任一部分不可序列化,则抛出该异常。

## 描述

参见 {{domxref("Window.structuredClone()")}} 以获取该函数的详细信息。

## 示例

参见 {{domxref("Window.structuredClone()")}} 以获取示例。

## 规范

{{Specifications}}

## 浏览器兼容性

{{Compat}}

## 参见

- [`core-js`](https://github.com/zloirock/core-js) 已经支持 [`structuredClone` 的 polyfill](https://github.com/zloirock/core-js#structuredclone)
- [结构化克隆算法](/zh-CN/docs/Web/API/Web_Workers_API/Structured_clone_algorithm)
- [结构化克隆的 polyfill](https://github.com/ungap/structured-clone)
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,7 @@ const fruitsCopy3 = fruits.slice();
const fruitsDeepCopy = JSON.parse(JSON.stringify(fruits));
```

你还可以使用 [`structuredClone()`](/zh-CN/docs/Web/API/structuredClone) 方法创建深拷贝,该方法的优点是允许源代码中的[可转移对象](/zh-CN/docs/Web/API/Web_Workers_API/Transferable_objects)*转移*到新的副本,而不仅仅是克隆。
你还可以使用 {{DOMxRef("Window.structuredClone", "structuredClone()")}} 方法创建深拷贝,该方法的优点是允许源代码中的[可转移对象](/zh-CN/docs/Web/API/Web_Workers_API/Transferable_objects)*转移*到新的副本,而不仅仅是克隆。

最后,重要的是要理解,将现有数组赋值给新变量并不会创建数组或其元素的副本。相反,新变量只是对原数组的引用或别名;也就是说,原来的数组名和新的变量名只是同一个对象的两个名称(因此总是被计算为[严格相等](/zh-CN/docs/Web/JavaScript/Equality_comparisons_and_sameness#严格相等))。因此,如果你对原数组的值或新变量的值做了任何改变,另一个也会改变:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ slug: Web/JavaScript/Reference/Global_Objects/EvalError

本对象代表了一个关于 {{jsxref("Global_Objects/eval", "eval()")}} 全局函数的错误。此异常不再会被 JavaScript 抛出,但是 EvalError 对象仍然存在,以保持兼容性。

`EvalError` 是一个{{Glossary("serializable object", "可序列化对象")}},所以可以使用 {{domxref("structuredClone()")}} 对它进行克隆,也可以使用 {{domxref("Worker/postMessage()", "postMessage()")}} 在 [Worker](/zh-CN/docs/Web/API/Worker) 之间拷贝它。
`EvalError` 是一个{{Glossary("serializable object", "可序列化对象")}},所以可以使用 {{DOMxRef("Window.structuredClone", "structuredClone()")}} 对它进行克隆,也可以使用 {{domxref("Worker/postMessage()", "postMessage()")}} 在 [Worker](/zh-CN/docs/Web/API/Worker) 之间拷贝它。

## 构造函数

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ JSON.stringify(circularReference);

要序列化循环引用,你可以使用支持循环引用的库(例如 Douglas Crockford 的 [cycle.js](https://github.com/douglascrockford/JSON-js/blob/master/cycle.js)),或者自己实现一个解决方案,这需要找到循环引用,并用可序列化的值替换(或移除)它们。

如果你在使用 `JSON.stringify()` 来深拷贝一个对象,你可能想要使用 [`structuredClone()`](/zh-CN/docs/Web/API/structuredClone),它支持循环引用。JavaScript 引擎的二进制序列化 API,比如 [`v8.serialize()`](https://nodejs.org/api/v8.html#v8serializevalue),也支持循环引用。
如果你在使用 `JSON.stringify()` 来深拷贝一个对象,你可能想要使用 {{DOMxRef("Window.structuredClone", "structuredClone()")}},它支持循环引用。JavaScript 引擎的二进制序列化 API,比如 [`v8.serialize()`](https://nodejs.org/api/v8.html#v8serializevalue),也支持循环引用。

### `JSON.stringify`用作 JavaScript

Expand Down
Loading

0 comments on commit 1ad0926

Please sign in to comment.