refactor objects, add optional chaining
This commit is contained in:
parent
09a964e969
commit
b057341f6c
35 changed files with 579 additions and 435 deletions
228
1-js/04-object-basics/02-object-copy/article.md
Normal file
228
1-js/04-object-basics/02-object-copy/article.md
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
# Object copying, references
|
||||
|
||||
One of the fundamental differences of objects vs primitives is that they are stored and copied "by reference".
|
||||
|
||||
Primitive values: strings, numbers, booleans -- are assigned/copied "as a whole value".
|
||||
|
||||
For instance:
|
||||
|
||||
```js
|
||||
let message = "Hello!";
|
||||
let phrase = message;
|
||||
```
|
||||
|
||||
As a result we have two independent variables, each one is storing the string `"Hello!"`.
|
||||
|
||||

|
||||
|
||||
Objects are not like that.
|
||||
|
||||
**A variable stores not the object itself, but its "address in memory", in other words "a reference" to it.**
|
||||
|
||||
Here's the picture for the object:
|
||||
|
||||
```js
|
||||
let user = {
|
||||
name: "John"
|
||||
};
|
||||
```
|
||||
|
||||

|
||||
|
||||
Here, the object is stored somewhere in memory. And the variable `user` has a "reference" to it.
|
||||
|
||||
**When an object variable is copied -- the reference is copied, the object is not duplicated.**
|
||||
|
||||
For instance:
|
||||
|
||||
```js no-beautify
|
||||
let user = { name: "John" };
|
||||
|
||||
let admin = user; // copy the reference
|
||||
```
|
||||
|
||||
Now we have two variables, each one with the reference to the same object:
|
||||
|
||||

|
||||
|
||||
We can use any variable to access the object and modify its contents:
|
||||
|
||||
```js run
|
||||
let user = { name: 'John' };
|
||||
|
||||
let admin = user;
|
||||
|
||||
*!*
|
||||
admin.name = 'Pete'; // changed by the "admin" reference
|
||||
*/!*
|
||||
|
||||
alert(*!*user.name*/!*); // 'Pete', changes are seen from the "user" reference
|
||||
```
|
||||
|
||||
The example above demonstrates that there is only one object. As if we had a cabinet with two keys and used one of them (`admin`) to get into it. Then, if we later use another key (`user`) we can see changes.
|
||||
|
||||
## Comparison by reference
|
||||
|
||||
The equality `==` and strict equality `===` operators for objects work exactly the same.
|
||||
|
||||
**Two objects are equal only if they are the same object.**
|
||||
|
||||
Here two variables reference the same object, thus they are equal:
|
||||
|
||||
```js run
|
||||
let a = {};
|
||||
let b = a; // copy the reference
|
||||
|
||||
alert( a == b ); // true, both variables reference the same object
|
||||
alert( a === b ); // true
|
||||
```
|
||||
|
||||
And here two independent objects are not equal, even though both are empty:
|
||||
|
||||
```js run
|
||||
let a = {};
|
||||
let b = {}; // two independent objects
|
||||
|
||||
alert( a == b ); // false
|
||||
```
|
||||
|
||||
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 occur very rarely, usually as a result of a coding mistake.
|
||||
|
||||
## Cloning and merging, Object.assign
|
||||
|
||||
So, copying an object variable creates one more reference to the same object.
|
||||
|
||||
But what if we need to duplicate an object? Create an independent copy, a clone?
|
||||
|
||||
That's also doable, but a little bit more difficult, because there's no built-in method for that in JavaScript. Actually, that's rarely needed. Copying by reference is good most of the time.
|
||||
|
||||
But if we really want that, then we need to create a new object and replicate the structure of the existing one by iterating over its properties and copying them on the primitive level.
|
||||
|
||||
Like this:
|
||||
|
||||
```js run
|
||||
let user = {
|
||||
name: "John",
|
||||
age: 30
|
||||
};
|
||||
|
||||
*!*
|
||||
let clone = {}; // the new empty object
|
||||
|
||||
// let's copy all user properties into it
|
||||
for (let key in user) {
|
||||
clone[key] = user[key];
|
||||
}
|
||||
*/!*
|
||||
|
||||
// now clone is a fully independent object with the same content
|
||||
clone.name = "Pete"; // changed the data in it
|
||||
|
||||
alert( user.name ); // still John in the original object
|
||||
```
|
||||
|
||||
Also we can use the method [Object.assign](mdn:js/Object/assign) for that.
|
||||
|
||||
The syntax is:
|
||||
|
||||
```js
|
||||
Object.assign(dest, [src1, src2, src3...])
|
||||
```
|
||||
|
||||
- The first argument `dest` is a target object.
|
||||
- Further arguments `src1, ..., srcN` (can be as many as needed) are source objects.
|
||||
- It copies the properties of all source objects `src1, ..., srcN` into the target `dest`. In other words, properties of all arguments starting from the second are copied into the first object.
|
||||
- The call returns `dest`.
|
||||
|
||||
For instance, we can use it to merge several objects into one:
|
||||
```js
|
||||
let user = { name: "John" };
|
||||
|
||||
let permissions1 = { canView: true };
|
||||
let permissions2 = { canEdit: true };
|
||||
|
||||
*!*
|
||||
// copies all properties from permissions1 and permissions2 into user
|
||||
Object.assign(user, permissions1, permissions2);
|
||||
*/!*
|
||||
|
||||
// now user = { name: "John", canView: true, canEdit: true }
|
||||
```
|
||||
|
||||
If the copied property name already exists, it gets overwritten:
|
||||
|
||||
```js run
|
||||
let user = { name: "John" };
|
||||
|
||||
Object.assign(user, { name: "Pete" });
|
||||
|
||||
alert(user.name); // now user = { name: "Pete" }
|
||||
```
|
||||
|
||||
We also can use `Object.assign` to replace `for..in` loop for simple cloning:
|
||||
|
||||
```js
|
||||
let user = {
|
||||
name: "John",
|
||||
age: 30
|
||||
};
|
||||
|
||||
*!*
|
||||
let clone = Object.assign({}, user);
|
||||
*/!*
|
||||
```
|
||||
|
||||
It copies all properties of `user` into the empty object and returns it.
|
||||
|
||||
## Nested cloning
|
||||
|
||||
Until now we assumed that all properties of `user` are primitive. But properties can be references to other objects. What to do with them?
|
||||
|
||||
Like this:
|
||||
```js run
|
||||
let user = {
|
||||
name: "John",
|
||||
sizes: {
|
||||
height: 182,
|
||||
width: 50
|
||||
}
|
||||
};
|
||||
|
||||
alert( user.sizes.height ); // 182
|
||||
```
|
||||
|
||||
Now it's not enough to copy `clone.sizes = user.sizes`, because the `user.sizes` is an object, it will be copied by reference. So `clone` and `user` will share the same sizes:
|
||||
|
||||
Like this:
|
||||
|
||||
```js run
|
||||
let user = {
|
||||
name: "John",
|
||||
sizes: {
|
||||
height: 182,
|
||||
width: 50
|
||||
}
|
||||
};
|
||||
|
||||
let clone = Object.assign({}, user);
|
||||
|
||||
alert( user.sizes === clone.sizes ); // true, same object
|
||||
|
||||
// user and clone share sizes
|
||||
user.sizes.width++; // change a property from one place
|
||||
alert(clone.sizes.width); // 51, see the result from the other one
|
||||
```
|
||||
|
||||
To fix that, we should use the 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".
|
||||
|
||||
There's a standard algorithm for deep cloning that handles the case above and more complex cases, called the [Structured cloning algorithm](https://html.spec.whatwg.org/multipage/structured-data.html#safe-passing-of-structured-data).
|
||||
|
||||
We can use recursion to implement it. Or, not to reinvent the wheel, take an existing implementation, for instance [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep) from the JavaScript library [lodash](https://lodash.com).
|
||||
|
||||
## Summary
|
||||
|
||||
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.
|
||||
|
||||
All operations via copied references (like adding/removing properties) are performed on the same single object.
|
||||
|
||||
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).
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="370" height="249" viewBox="0 0 370 249"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="combined" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="variable-contains-reference.svg"><g id="noun_1211_cc" fill="#E8C48F" transform="translate(12 119)"><path id="Shape" d="M17 28.196h112.558v42.95c0 .373-.079.862-.279 1.294-.2.433-16.574 35.56-16.574 35.56V53.64l16.854-25.444L148 0H35.44L17 28.196zM17 57V29L2 57"/><path id="Shape" d="M0 59v54.73c0 3.42 1.484 5.27 4.387 5.27h100.086c3.122 0 5.527-2.548 5.527-3.476V59H0z"/></g><text id="user" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="47" y="215">user</tspan></text><path id="Rectangle-4" fill="#FFF" d="M116 153h44v6h-44z"/><path id="Shape-3" fill="#EE6B47" d="M123.854 154.322l13.779-13.476c1.613-1.274 5.185-4.427 3.572-5.702-1.613-1.275-2.664-.472-4.275.803l-18.499 18.375c-1.613 1.275-1.613 2.845 0 4.12 0 0 21.483 20.067 22.774 20.067 3.005 0 2.677-1.821 1.064-3.096l-18.415-17.826H201.028c2.281 0 3.75.279 3.75-1.524 0-1.804-1.469-1.741-3.75-1.741h-77.174z" transform="matrix(-1 0 0 1 322 0)"/><g id="Rectangle-3-+-Shape" transform="translate(58 135)"><path id="Rectangle-3" fill="#FFF" d="M0 0h47v37H0z"/><path id="Shape" fill="#EE6B47" d="M44 33.772H4V4h40v29.772zM8.733 31.018h30.533L29.22 20.868l-1.902 1.889c-.824.812-2.003 1.277-3.242 1.277l-.021-.001c-1.244-.005-2.427-.48-3.246-1.304l-1.934-1.933-10.14 10.222zM6.791 8.726V29.04l10.122-10.202L6.79 8.726zm24.395 10.187L41.209 29.04V8.954l-10.023 9.96zM8.74 6.755l14.057 14.042c.304.305.766.48 1.27.482h.008c.496 0 .968-.173 1.264-.467L39.49 6.755H8.739z"/></g><path id="Rectangle-4-Copy" fill="#D1C4B1" stroke="#D1C4B1" stroke-width="4" d="M241.937 117l-16.667 20h130.46l-16.667-20h-97.126z" opacity=".5"/><g id="Group-2" transform="translate(271 10)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v124H0z"/><path id="Combined-Shape" fill="#E8C48F" d="M32 0v124H0V0h32zM16.5 100a7.5 7.5 0 100 15 7.5 7.5 0 000-15zM28 5H4v87h24V5z"/></g><text id="name" fill="#EE6B47" font-family="PTMono-Bold, PT Mono" font-size="16" font-weight="bold" transform="rotate(-90 15.5 47.5)"><tspan x="-3.7" y="52.5">name</tspan></text></g><path id="Rectangle-4" fill="#FFF9EB" stroke="#BCA68E" stroke-width="4" d="M223 131h135v50H223z"/><path id="Rectangle-8" stroke="#BCA68E" stroke-width="3" d="M278.5 151.5h25v10h-25z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="599" height="260" viewBox="0 0 599 260"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="combined" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="variable-copy-reference.svg"><g id="Group" transform="translate(11 125)"><g id="noun_1211_cc" fill="#E8C48F"><path id="Shape" d="M17 28.196h112.558v42.95c0 .373-.079.862-.279 1.294-.2.433-16.574 35.56-16.574 35.56V53.64l16.854-25.444L148 0H35.44L17 28.196zM17 57V29L2 57"/><path id="Shape" d="M0 59v54.73c0 3.42 1.484 5.27 4.387 5.27h100.086c3.122 0 5.527-2.548 5.527-3.476V59H0z"/></g><text id="user" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="35" y="96">user</tspan></text><g id="Rectangle-4-+-Shape-3" transform="translate(104 15)"><path id="Rectangle-4" fill="#FFF" d="M0 19h44v6H0z"/><path id="Shape-3" fill="#EE6B47" d="M7.854 20.322L21.633 6.846c1.613-1.274 5.185-4.427 3.572-5.702-1.613-1.275-2.664-.472-4.275.803L2.43 20.322c-1.613 1.275-1.613 2.845 0 4.12 0 0 21.483 20.067 22.774 20.067 3.005 0 2.677-1.821 1.064-3.096L7.854 23.587H85.028c2.281 0 3.75.279 3.75-1.524 0-1.804-1.469-1.741-3.75-1.741H7.854z" transform="matrix(-1 0 0 1 90 0)"/></g><g id="Rectangle-3-+-Shape" transform="translate(46 16)"><path id="Rectangle-3" fill="#FFF" d="M0 0h47v37H0z"/><path id="Shape" fill="#EE6B47" d="M44 33.772H4V4h40v29.772zM8.733 31.018h30.533L29.22 20.868l-1.902 1.889c-.824.812-2.003 1.277-3.242 1.277l-.021-.001c-1.244-.005-2.427-.48-3.246-1.304l-1.934-1.933-10.14 10.222zM6.791 8.726V29.04l10.122-10.202L6.79 8.726zm24.395 10.187L41.209 29.04V8.954l-10.023 9.96zM8.74 6.755l14.057 14.042c.304.305.766.48 1.27.482h.008c.496 0 .968-.173 1.264-.467L39.49 6.755H8.739z"/></g></g><g id="noun_1211_cc" fill="#E8C48F" transform="translate(418 125)"><path id="Shape" d="M17 28.196h112.558v42.95c0 .373-.079.862-.279 1.294-.2.433-16.574 35.56-16.574 35.56V53.64l16.854-25.444L148 0H35.44L17 28.196zM17 57V29L2 57"/><path id="Shape" d="M0 59v54.73c0 3.42 1.484 5.27 4.387 5.27h100.086c3.122 0 5.527-2.548 5.527-3.476V59H0z"/></g><text id="admin" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="446" y="221">admin</tspan></text><g id="Rectangle-4-+-Shape-3" transform="matrix(-1 0 0 1 451 140)"><path id="Rectangle-4" fill="#FFF" d="M0 19h44v6H0z"/><path id="Shape-3" fill="#EE6B47" d="M7.854 20.322L21.633 6.846c1.613-1.274 5.185-4.427 3.572-5.702-1.613-1.275-2.664-.472-4.275.803L2.43 20.322c-1.613 1.275-1.613 2.845 0 4.12 0 0 21.483 20.067 22.774 20.067 3.005 0 2.677-1.821 1.064-3.096L7.854 23.587H85.028c2.281 0 3.75.279 3.75-1.524 0-1.804-1.469-1.741-3.75-1.741H7.854z" transform="matrix(-1 0 0 1 90 0)"/></g><g id="Rectangle-3-+-Shape" transform="translate(464 141)"><path id="Rectangle-3" fill="#FFF" d="M0 0h47v37H0z"/><path id="Shape" fill="#EE6B47" d="M44 33.772H4V4h40v29.772zM8.733 31.018h30.533L29.22 20.868l-1.902 1.889c-.824.812-2.003 1.277-3.242 1.277l-.021-.001c-1.244-.005-2.427-.48-3.246-1.304l-1.934-1.933-10.14 10.222zM6.791 8.726V29.04l10.122-10.202L6.79 8.726zm24.395 10.187L41.209 29.04V8.954l-10.023 9.96zM8.74 6.755l14.057 14.042c.304.305.766.48 1.27.482h.008c.496 0 .968-.173 1.264-.467L39.49 6.755H8.739z"/></g><path id="Rectangle-4-Copy" fill="#D1C4B1" stroke="#D1C4B1" stroke-width="4" d="M235.937 125l-16.667 20h130.46l-16.667-20h-97.126z" opacity=".5"/><g id="Group-2" transform="translate(265 18)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v124H0z"/><path id="Combined-Shape" fill="#E8C48F" d="M32 0v124H0V0h32zM16.5 100a7.5 7.5 0 100 15 7.5 7.5 0 000-15zM28 5H4v87h24V5z"/></g><text id="name" fill="#EE6B47" font-family="PTMono-Bold, PT Mono" font-size="16" font-weight="bold" transform="rotate(-90 15.5 47.5)"><tspan x="-3.7" y="52.5">name</tspan></text></g><path id="Rectangle-4" fill="#FFF9EB" stroke="#BCA68E" stroke-width="4" d="M217 139h135v50H217z"/><path id="Rectangle-8" stroke="#BCA68E" stroke-width="3" d="M272.5 159.5h25v10h-25z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="359" height="143" viewBox="0 0 359 143"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="combined" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="variable-copy-value.svg"><g id="noun_1211_cc-+-Message" transform="translate(11 6)"><g id="noun_1211_cc"><path id="Shape" fill="#E8C48F" d="M17 37.196h112.558v42.95c0 .373-.079.862-.279 1.294-.2.433-16.574 35.56-16.574 35.56V62.64l16.854-25.444L148 9H35.44L17 37.196zM17 66V38L2 66"/><g id="Rectangle-5-+-"World!"" transform="translate(15)"><path id="Rectangle-5" fill="#FFF9EB" stroke="#8A704D" stroke-width="2" d="M18.861 1.809L2 17.533l53.14 56.986L72 58.794 18.861 1.81z"/><text id=""Hello!"" fill="#8A704D" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold" transform="rotate(47 38.202 38.946)"><tspan x="2.822" y="43.482">"Hello!"</tspan></text></g><path id="Shape" fill="#E8C48F" d="M0 68v54.73c0 3.42 1.484 5.27 4.387 5.27h100.086c3.122 0 5.527-2.548 5.527-3.476V68H0z"/></g><text id="message" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="17" y="105">message</tspan></text></g><g id="Group" transform="translate(210 6)"><path id="Shape" fill="#E8C48F" d="M17 37.196h113.417v42.95c0 .373-.08.862-.28 1.294-.202.433-16.702 35.56-16.702 35.56V62.64l16.983-25.444L149 9H35.582L17 37.196z"/><path id="Shape" fill="#E8C48F" d="M18 66V38L2 66"/><g id="Rectangle-5-+-"World!"" transform="translate(15)"><path id="Rectangle-5" fill="#FFF9EB" stroke="#8A704D" stroke-width="2" d="M19.117 1.8l-17.1 15.734 53.866 56.994 17.1-15.734L19.118 1.799z"/><text id=""Hello!"" fill="#8A704D" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold" transform="rotate(47 38.162 37.693)"><tspan x="2.782" y="42.23">"Hello!"</tspan></text></g><path id="Shape" fill="#E8C48F" d="M0 68v54.73c0 3.42 1.497 5.27 4.427 5.27h100.996c3.15 0 5.577-2.548 5.577-3.476V68H0z"/><text id="phrase" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="25" y="105">phrase</tspan></text></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
Loading…
Add table
Add a link
Reference in a new issue