Skip to content

Commit 53b35c1

Browse files
committed
closes #3129
1 parent 9312769 commit 53b35c1

File tree

1 file changed

+74
-16
lines changed

1 file changed

+74
-16
lines changed

1-js/04-object-basics/02-object-copy/article.md

+74-16
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,30 @@ alert( a == b ); // false
100100
101101
For comparisons like `obj1 > obj2` or for a comparison against a primitive `obj == 5`, objects are converted to primitives. We'll study how object conversions work very soon, but to tell the truth, such comparisons are needed very rarely -- usually they appear as a result of a programming mistake.
102102
103+
````smart header="Const objects can be modified"
104+
An important side effect of storing objects as references is that an object declared as `const` *can* be modified.
105+
106+
For instance:
107+
108+
```js run
109+
const user = {
110+
name: "John"
111+
};
112+
113+
*!*
114+
user.name = "Pete"; // (*)
115+
*/!*
116+
117+
alert(user.name); // Pete
118+
```
119+
120+
It might seem that the line `(*)` would cause an error, but it does not. The value of `user` is constant, it must always reference the same object, but properties of that object are free to change.
121+
122+
In other words, the `const user` gives an error only if we try to set `user=...` as a whole.
123+
124+
That said, if we really need to make constant object properties, it's also possible, but using totally different methods. We'll mention that in the chapter <info:property-descriptors>.
125+
````
126+
103127
## Cloning and merging, Object.assign [#cloning-and-merging-object-assign]
104128

105129
So, copying an object variable creates one more reference to the same object.
@@ -219,42 +243,76 @@ let clone = Object.assign({}, user);
219243
alert( user.sizes === clone.sizes ); // true, same object
220244
221245
// user and clone share sizes
222-
user.sizes.width++; // change a property from one place
223-
alert(clone.sizes.width); // 51, get the result from the other one
246+
user.sizes.width = 60; // change a property from one place
247+
alert(clone.sizes.width); // 60, get the result from the other one
224248
```
225249
226-
To fix that and make `user` and `clone` truly separate objects, we should use a cloning loop that examines each value of `user[key]` and, if it's an object, then replicate its structure as well. That is called a "deep cloning".
250+
To fix that and make `user` and `clone` truly separate objects, we should use a cloning loop that examines each value of `user[key]` and, if it's an object, then replicate its structure as well. That is called a "deep cloning" or "structured cloning". There's [structuredClone](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) method that implements deep cloning.
227251
228-
We can use recursion to implement it. Or, to not reinvent the wheel, take an existing implementation, for instance [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep) from the JavaScript library [lodash](https://lodash.com).
229252
230-
````smart header="Const objects can be modified"
231-
An important side effect of storing objects as references is that an object declared as `const` *can* be modified.
253+
### structuredClone
232254
233-
For instance:
255+
The call `structuredClone(object)` clones the `object` with all nested properties.
256+
257+
Here's how we can use it in our example:
234258

235259
```js run
236-
const user = {
237-
name: "John"
260+
let user = {
261+
name: "John",
262+
sizes: {
263+
height: 182,
264+
width: 50
265+
}
238266
};
239267
240268
*!*
241-
user.name = "Pete"; // (*)
269+
let clone = structuredClone(user);
242270
*/!*
243271
244-
alert(user.name); // Pete
272+
alert( user.sizes === clone.sizes ); // false, different objects
273+
274+
// user and clone are totally unrelated now
275+
user.sizes.width = 60; // change a property from one place
276+
alert(clone.sizes.width); // 50, not related
245277
```
246278

247-
It might seem that the line `(*)` would cause an error, but it does not. The value of `user` is constant, it must always reference the same object, but properties of that object are free to change.
279+
The `structuredClone` method can clone most data types, such as objects, arrays, primitive values.
248280

249-
In other words, the `const user` gives an error only if we try to set `user=...` as a whole.
281+
It also supports circular references, when an object property references the object itself (directly or via a chain or references).
250282

251-
That said, if we really need to make constant object properties, it's also possible, but using totally different methods. We'll mention that in the chapter <info:property-descriptors>.
252-
````
283+
For instance:
284+
285+
```js run
286+
let user = {};
287+
// let's create a circular reference:
288+
// user.me references the user itself
289+
user.me = user;
290+
291+
let clone = structuredClone(user);
292+
alert(clone.me === clone); // true
293+
```
294+
295+
As you can see, `clone.me` references the `clone`, not the `user`! So the circular reference was cloned correctly as well.
296+
297+
Although, there are cases when `structuredClone` fails.
298+
299+
For instance, when an object has a function property:
300+
301+
```js run
302+
// error
303+
structuredClone({
304+
f: function() {}
305+
});
306+
```
307+
308+
Function properties aren't supported.
309+
310+
To handle such complex cases we may need to use a combination of cloning methods, write custom code or, to not reinvent the wheel, take an existing implementation, for instance [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep) from the JavaScript library [lodash](https://lodash.com).
253311

254312
## Summary
255313

256314
Objects are assigned and copied by reference. In other words, a variable stores not the "object value", but a "reference" (address in memory) for the value. So copying such a variable or passing it as a function argument copies that reference, not the object itself.
257315

258316
All operations via copied references (like adding/removing properties) are performed on the same single object.
259317

260-
To make a "real copy" (a clone) we can use `Object.assign` for the so-called "shallow copy" (nested objects are copied by reference) or a "deep cloning" function, such as [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep).
318+
To make a "real copy" (a clone) we can use `Object.assign` for the so-called "shallow copy" (nested objects are copied by reference) or a "deep cloning" function `structuredClone` or use a custom cloning implementation, such as [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep).

0 commit comments

Comments
 (0)