You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 1-js/04-object-basics/02-object-copy/article.md
+74-16
Original file line number
Diff line number
Diff line change
@@ -100,6 +100,30 @@ alert( a == b ); // false
100
100
101
101
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.
102
102
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* bemodified.
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
+
103
127
## Cloning and merging, Object.assign [#cloning-and-merging-object-assign]
104
128
105
129
So, copying an object variable creates one more reference to the same object.
@@ -219,42 +243,76 @@ let clone = Object.assign({}, user);
219
243
alert( user.sizes === clone.sizes ); // true, same object
220
244
221
245
// 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
224
248
```
225
249
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.
227
251
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).
229
252
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* bemodified.
253
+
### structuredClone
232
254
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:
234
258
235
259
```js run
236
-
const user = {
237
-
name: "John"
260
+
let user = {
261
+
name: "John",
262
+
sizes: {
263
+
height: 182,
264
+
width: 50
265
+
}
238
266
};
239
267
240
268
*!*
241
-
user.name = "Pete"; // (*)
269
+
let clone = structuredClone(user);
242
270
*/!*
243
271
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
245
277
```
246
278
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.
248
280
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).
250
282
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).
253
311
254
312
## Summary
255
313
256
314
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.
257
315
258
316
All operations via copied references (like adding/removing properties) are performed on the same single object.
259
317
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