|
| 1 | +# Object references and copying |
| 2 | + |
| 3 | +[ref](https://javascript.info/object-copy) |
| 4 | + |
| 5 | +One of the fundamental differences of objects versus primitives is that objects are stored and copied “by reference”, whereas primitive values: strings, numbers, booleans, etc – are always copied “as a whole value”. |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +**But, copying an object variable creates one more reference to the same object.** |
| 10 | + |
| 11 | + |
| 12 | + |
| 13 | +# Comparison by reference |
| 14 | + |
| 15 | +Two objects are equal only if they are the same object. |
| 16 | + |
| 17 | +For instance, here a and b reference the same object, thus they are equal: |
| 18 | + |
| 19 | +```javascript |
| 20 | +let a = {}; |
| 21 | +let b = a; // copy the reference |
| 22 | + |
| 23 | +alert(a == b); // true, both variables reference the same object |
| 24 | +alert(a === b); // true |
| 25 | +``` |
| 26 | + |
| 27 | +And here two independent objects are not equal, even though they look alike (both are empty): |
| 28 | + |
| 29 | +```javascript |
| 30 | +let a = {}; |
| 31 | +let b = {}; // two independent objects |
| 32 | + |
| 33 | +alert(a == b); // false |
| 34 | +``` |
| 35 | + |
| 36 | +For comparisons like obj1 > obj2 or for a comparison against a primitive obj == 5, objects are converted to primitives. |
| 37 | + |
| 38 | +# Cloning and merging, Object.assign |
| 39 | + |
| 40 | +If we need to duplicate an object? Create an independent copy, a clone? |
| 41 | + |
| 42 | +we can use following methods for that. |
| 43 | + |
| 44 | +1. spread syntax |
| 45 | +2. Object.assign |
| 46 | + |
| 47 | +## 1. spread syntax |
| 48 | + |
| 49 | +```javascript |
| 50 | +let obj = { a: 1, b: 2, c: 3 }; |
| 51 | + |
| 52 | +// spread the object into a list of parameters |
| 53 | +// then return the result in a new object |
| 54 | + |
| 55 | +let objCopy = { ...obj }; |
| 56 | +``` |
| 57 | + |
| 58 | +## 2. Object.assign |
| 59 | + |
| 60 | +The syntax is: |
| 61 | + |
| 62 | +```javascript |
| 63 | +Object.assign(dest, [src1, src2, src3...]) |
| 64 | +``` |
| 65 | + |
| 66 | +here's how we can use it to merge several objects into one |
| 67 | + |
| 68 | +```javascript |
| 69 | +let user = { name: 'John' }; |
| 70 | + |
| 71 | +let permissions1 = { canView: true }; |
| 72 | +let permissions2 = { canEdit: true }; |
| 73 | + |
| 74 | +// copies all properties from permissions1 and permissions2 into user |
| 75 | +Object.assign(user, permissions1, permissions2); |
| 76 | + |
| 77 | +// now user = { name: "John", canView: true, canEdit: true } |
| 78 | +``` |
| 79 | + |
| 80 | +If the copied property name already exists, it gets overwritten: |
| 81 | + |
| 82 | +```javascript |
| 83 | +let user = { name: 'John' }; |
| 84 | + |
| 85 | +Object.assign(user, { name: 'Pete' }); |
| 86 | + |
| 87 | +alert(user.name); // now user = { name: "Pete" } |
| 88 | +``` |
| 89 | + |
| 90 | +following copies all properties of user into the empty object and returns it. |
| 91 | + |
| 92 | +```javascript |
| 93 | +let user = { |
| 94 | + name: 'John', |
| 95 | + age: 30, |
| 96 | +}; |
| 97 | + |
| 98 | +let clone = Object.assign({}, user); |
| 99 | +``` |
| 100 | + |
| 101 | +# Nested cloning |
| 102 | + |
| 103 | +Until now we assumed that all properties of user are primitive. But properties can be references to other objects. How they will be copied? |
| 104 | + |
| 105 | +**Answer: They will be copied by reference.** |
| 106 | + |
| 107 | +```javascript |
| 108 | +let user = { |
| 109 | + name: 'John', |
| 110 | + sizes: { |
| 111 | + height: 182, |
| 112 | + width: 50, |
| 113 | + }, |
| 114 | +}; |
| 115 | + |
| 116 | +let clone = Object.assign({}, user); |
| 117 | + |
| 118 | +alert(user === clone); // FALSE, DIFFERENT OBJECT *****NOTE**** |
| 119 | + |
| 120 | +alert(user.sizes === clone.sizes); // true, same object |
| 121 | + |
| 122 | +// user and clone share sizes |
| 123 | +user.sizes.width++; // change a property from one place |
| 124 | +alert(clone.sizes.width); // 51, see the result from the other one |
| 125 | +``` |
| 126 | + |
| 127 | +**How to resolve?** |
| 128 | + |
| 129 | +To fix that, 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”. |
| 130 | + |
| 131 | +We can use recursion to implement it. Or, to not reinvent the wheel, take an existing implementation, for instance `_.cloneDeep(obj)` from the JavaScript library `lodash`. |
| 132 | + |
| 133 | +# Side effect of storing objects as references |
| 134 | + |
| 135 | +An important side effect of storing objects as references is that an object declared as `const` can be modified. |
| 136 | + |
| 137 | +```javascript |
| 138 | +const user = { |
| 139 | + name: 'John', |
| 140 | +}; |
| 141 | + |
| 142 | +user.name = 'Pete'; // (*) |
| 143 | + |
| 144 | +alert(user.name); // Pete |
| 145 | +``` |
| 146 | + |
| 147 | +The value of `user` is constant, it must always reference the same object, but properties of that object are free to change. |
| 148 | + |
| 149 | +In other words, the const user gives an error only if we try to set `user=...` as a whole. |
| 150 | + |
| 151 | +// TODO [take notes from here - property-descriptors](https://javascript.info/property-descriptors) |
| 152 | + |
| 153 | +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 Property flags and descriptors. |
0 commit comments