Skip to content

Commit

Permalink
Add new “Shallow copy” and “Deep copy” glossary entries (#12474)
Browse files Browse the repository at this point in the history
Co-authored-by: Hamish Willee <hamishwillee@gmail.com>
Co-authored-by: Daniel D. Beck <dbeck@mozilla.com>
  • Loading branch information
3 people committed Feb 4, 2022
1 parent b81ff9b commit 80ac011
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 14 deletions.
36 changes: 36 additions & 0 deletions files/en-us/glossary/deep_copy/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
title: Deep copy
slug: Glossary/Deep_copy
tags:
- Glossary
- Deep copy
---
{{MDNSidebar}}

A **deep copy** of an object is a copy whose properties do not share the same references (point to the same underlying values) as those of the source object from which the copy was made. As a result, when you change either the source or the copy, you can be assured you’re not causing the other object to change too; that is, you won’t unintentionally be causing changes to the source or copy that you don’t expect. That behavior contrasts with the behavior of a [shallow copy](/en-US/docs/Glossary/Shallow_copy), in which changes to either the source or the copy may also cause the other object to change too (because the two objects share the same references).

In JavaScript, standard built-in object-copy operations ([spread syntax](/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax), [`Array.prototype.concat()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat), [`Array.prototype.slice()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice), [`Array.from()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from), [`Object.assign()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign), and [`Object.create()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create)) do not create deep copies (instead, they create shallow copies).

One way to make a deep copy of a JavaScript object, if it can be [serialized](/en-US/docs/Glossary/Serialization), is to use {{jsxref("JSON.stringify()")}} to convert the object to a JSON string, and then {{jsxref("JSON.parse()")}} to convert the string back into a (completely new) JavaScript object:

```js
let ingredients_list = ["noodles",{"list":["eggs","flour","water"]}];
let ingredients_list_deepcopy = JSON.parse(JSON.stringify(ingredients_list));

// Change the value of the 'list' property in ingredients_list_copy.
ingredients_list_deepcopy[1].list = ["rice flour","water"]
// The 'list' property does not change in ingredients_list.
console.log(ingredients_list[1].list);
// Array(3) [ "eggs", "flour", "water" ]
```

As can be seen from the code above, because a deep copy shares no references with its source object, any changes made to the deep copy do not affect the source object.

However, while the object in the code above is simple enough to be [serializable](/en-US/docs/Glossary/Serialization), many JavaScript objects are not serializable at all — for example, [functions](/en-US/docs/Web/JavaScript/Guide/Functions) (with closures), [Symbols](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol), objects that represent HTML elements in the [HTML DOM API](/en-US/docs/Web/API/HTML_DOM_API), recursive data, any many other cases. Calling `JSON.stringify()` to serialize the objects in those cases will fail. So there’s no way to make deep copies of such objects.

For objects that _are_ serializable, you can alternatively use the [`structuredClone()`](/en-US/docs/Web/API/structuredClone) method to create deep copies. `structuredClone()` has the advantage of allowing {{Glossary("transferable objects")}} in the source to be _transferred_ to the new copy, rather than just cloned. But note that `structuredClone()` isn’t a feature of the JavaScript language itself — instead it’s a feature of browsers and any other JavaScript runtimes that implement a global object like [`window`](/en-US/docs/Web/API/Window). And calling `structuredClone()` to clone a non-serializable object will fail in the same way that calling `JSON.stringify()` to serialize it will fail.

## See also

- [Shallow copy](/en-US/docs/Glossary/Shallow_copy)
- [`window.structuredClone()`](/en-US/docs/Web/API/structuredClone)
56 changes: 56 additions & 0 deletions files/en-us/glossary/shallow_copy/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
title: Shallow copy
slug: Glossary/Shallow_copy
tags:
- Glossary
- Shallow copy
---
{{MDNSidebar}}

A **shallow copy** of an object is a copy whose properties share the same references (point to the same underlying values) as those of the source object from which the copy was made. As a result, when you change either the source or the copy, you may also cause the other object to change too — and so, you may end up unintentionally causing changes to the source or copy that you don’t expect. That behavior contrasts with the behavior of a [deep copy](/en-US/docs/Glossary/Deep_copy), in which the source and copy are completely independent.

For shallow copies, it’s important to understand that selectively changing the value of a shared property of an existing element in an object is different from assigning a completely new value to an existing element.

For example, if in a shallow copy named `copy` of an array object, the value of the `copy[0]` element is `{"list":["butter","flour"]}`, and you do `copy[0].list = ["oil","flour"]`, then the corresponding element in the source object will change, too — because you selectively changed a property of an object shared by both the source object and the shallow copy.

However, if instead you do `copy[0] = {"list":["oil","flour"]}`, then the corresponding element in the source object **will not change** — because in that case, you’re not just selectively changing a property of an existing array element that the shallow copy shares with the source object; instead you’re actually assigning a completely new value to that `copy[0]` array element, just in the shallow copy.

In JavaScript, all standard built-in object-copy operations ([spread syntax](/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax), [`Array.prototype.concat()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat), [`Array.prototype.slice()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice), [`Array.from()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from), [`Object.assign()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign), and [`Object.create()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create)) create shallow copies rather than deep copies.

## Example

Consider the following example, in which an `ingredients_list` array object is created, and then an `ingredients_list_copy` object is created by copying that `ingredients_list` object.

```js
let ingredients_list = ["noodles",{"list":["eggs","flour","water"]}];

let ingredients_list_copy = Array.from(ingredients_list);
console.log(JSON.stringify(ingredients_list_copy));
// ["noodles",{"list":["eggs","flour","water"]}]
```

Changing the value of the `list` property in `ingredients_list_copy` will also cause the `list` property to change in the `ingredients_list` source object.

```js
ingredients_list_copy[1].list = ["rice flour","water"]
console.log(ingredients_list[1].list);
// Array [ "rice flour", "water" ]
console.log(JSON.stringify(ingredients_list));
// ["noodles",{"list":["rice flour","water"]}]
```

Assigning a completely new value to the first element in `ingredients_list_copy` will not cause any change to the first element in the `ingredients_list` source object.

```js
ingredients_list_copy[0] = "rice noodles"
console.log(ingredients_list[0])
// noodles
console.log(JSON.stringify(ingredients_list_copy));
// ["rice noodles",{"list":["rice flour","water"]}]
console.log(JSON.stringify(ingredients_list));
// ["noodles",{"list":["rice flour","water"]}]
```

## See also

- [Deep copy](/en-US/docs/Glossary/Deep_copy)
6 changes: 3 additions & 3 deletions files/en-us/web/api/structuredclone/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ browser-compat: api.structuredClone
---
{{APIRef("HTML DOM")}}

The global **`structuredClone()`** method creates a deep clone of a given value using the [structured clone algorithm](/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm).
The global **`structuredClone()`** method creates a [deep clone](/en-US/docs/Glossary/Deep_copy) of a given value using the [structured clone algorithm](/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm).

The method also allows {{Glossary("transferable objects")}} in the original value to be _transferred_ rather than cloned to the new object.
Transferred objects are detached from the original object and attached to the new object; they are no longer accessible in the original object.
Expand All @@ -35,7 +35,7 @@ structuredClone(value, { transfer })

### Return value

The returned value is a deep copy of the original `value`.
The returned value is a [deep copy](/en-US/docs/Glossary/Deep_copy) of the original `value`.

### Exceptions

Expand All @@ -44,7 +44,7 @@ The returned value is a deep copy of the original `value`.

## Description

This function can be used to deep copy JavaScript values.
This function can be used to [deep copy](/en-US/docs/Glossary/Deep_copy) JavaScript values.
It also supports circular references, as shown below:

```js
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ concat(value0, value1, ... , valueN)
- `valueN` {{optional_inline}}
- : Arrays and/or values to concatenate into a new array. If all
`valueN` parameters are omitted, `concat` returns a
shallow copy of the existing array on which it is called. See the description below
[shallow copy](/en-us/docs/Glossary/Shallow_copy) of the existing array on which it is called. See the description below
for more details.

### Return value
Expand All @@ -47,7 +47,7 @@ that argument (if the argument is an array) or the argument itself (if the argum
not an array). It does not recurse into nested array arguments.

The `concat` method does not alter `this` or any of the arrays
provided as arguments but instead returns a shallow copy that contains copies of the
provided as arguments but instead returns a [shallow copy](/en-us/docs/Glossary/Shallow_copy) that contains copies of the
same elements combined from the original arrays. Elements of the original arrays are
copied into the new array as follows:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ let shallowCopySpread = [...fruits]
// ["Strawberry", "Mango"]
```

This is a shallow copy made using the [spread sequence](/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) operator:
This is a [shallow copy](/en-US/docs/Glossary/Shallow_copy) made using the [spread sequence](/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) operator:
Other ways to copy an array are discussed below in [Copying an array](#copying_an_array).

### Accessing array elements
Expand Down Expand Up @@ -309,9 +309,9 @@ let shallowCopyFrom = Array.from(fruits)
// ["Strawberry", "Mango"]
```

These all create a _shallow copy_; top-level elements containing primitive values are copied, but if the array contains nested objects or arrays, those will reference elements in the original array.
These all create a [_shallow copy_](/en-US/docs/Glossary/Shallow_copy); top-level elements containing primitive values are copied, but if the array contains nested objects or arrays, those will reference elements in the original array.

If you need a _deep copy_ of all elements — that is, in which even nested arrays don’t just reference elements in the original array but instead are also copied — one approach is to use {{jsxref("JSON.stringify()")}} to convert the array to a JSON string, and then {{jsxref("JSON.parse()")}} to convert the string back into an array.
If you need a [_deep copy_](/en-US/docs/Glossary/Deep_copy) of all elements — that is, in which even nested arrays don’t just reference elements in the original array but instead are also copied — one approach is to use {{jsxref("JSON.stringify()")}} to convert the array to a JSON string, and then {{jsxref("JSON.parse()")}} to convert the string back into an array.

```js
let deepCopy = JSON.parse(JSON.stringify(fruits));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ browser-compat: javascript.builtins.Array.slice
---
{{JSRef}}

The **`slice()`** method returns a shallow copy of a portion of
The **`slice()`** method returns a [shallow copy](/en-us/docs/Glossary/Shallow_copy) of a portion of
an array into a new array object selected from `start` to `end`
(`end` not included) where `start` and `end` represent
the index of items in that array. The original array will not be modified.
Expand Down Expand Up @@ -65,7 +65,7 @@ A new array containing the extracted elements.

## Description

`slice` does not alter the original array. It returns a shallow copy of
`slice` does not alter the original array. It returns a [shallow copy](/en-us/docs/Glossary/Shallow_copy) of
elements from the original array. Elements of the original array are copied into the
returned array as follows:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ console.log(copy); // { a: 1 }

### Warning for Deep Clone

For deep cloning, we need to use alternatives, because `Object.assign()`
For [deep cloning](/en-us/docs/Glossary/Deep_copy), we need to use alternatives, because `Object.assign()`
copies property values.

If the source value is a reference to an object, it only copies the reference value.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ A _property descriptor_ is a record with some of the following attributes:

## Examples

### Creating a shallow clone
### Creating a shallow copy

Whereas the {{jsxref("Object.assign()")}} method will only copy enumerable and own
properties from a source object to a target object, you are able to use this method and
{{jsxref("Object.create()")}} for a shallow copy between two unknown objects:
{{jsxref("Object.create()")}} for a [shallow copy](/en-US/docs/Glossary/Shallow_copy) between two unknown objects:

```js
Object.create(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ A new typed array containing the extracted elements.

## Description

The `slice` method does not alter the original typed array, but instead returns a copy of a portion of the original typed array. As typed arrays only store primitive values, the copy the `slice` method returns is always a shallow copy.
The `slice` method does not alter the original typed array, but instead returns a copy of a portion of the original typed array. As typed arrays only store primitive values, the copy the `slice` method returns is always a [shallow copy](/en-us/docs/Glossary/Shallow_copy).

If an element is changed in either typed array, the other typed array is not affected.

Expand Down

0 comments on commit 80ac011

Please sign in to comment.