# Object references and copying 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". That's easy to understand if we look a bit under the hood of what happens when we copy a value. Let's start with a primitive, such as a string. Here we put a copy of `message` into `phrase`: ```js let message = "Hello!"; let phrase = message; ``` As a result we have two independent variables, each one storing the string `"Hello!"`. ![](variable-copy-value.svg) Quite an obvious result, right? Objects are not like that. **A variable assigned to an object stores not the object itself, but its "address in memory" -- in other words "a reference" to it.** Let's look at an example of such a variable: ```js let user = { name: "John" }; ``` And here's how it's actually stored in memory: ![](variable-contains-reference.svg) The object is stored somewhere in memory (at the right of the picture), while the `user` variable (at the left) has a "reference" to it. We may think of an object variable, such as `user`, like a sheet of paper with the address of the object on it. When we perform actions with the object, e.g. take a property `user.name`, the JavaScript engine looks at what's at that address and performs the operation on the actual object. Now here's why it's important. **When an object variable is copied, the reference is copied, but the object itself is not duplicated.** For instance: ```js no-beautify let user = { name: "John" }; let admin = user; // copy the reference ``` Now we have two variables, each storing a reference to the same object: ![](variable-copy-reference.svg) As you can see, there's still one object, but now with two variables that reference it. We can use either 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 ``` It's as if we had a cabinet with two keys and used one of them (`admin`) to get into it and make changes. Then, if we later use another key (`user`), we are still opening the same cabinet and can access the changed contents. ## Comparison by reference Two objects are equal only if they are the same object. For instance, here `a` and `b` 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 they look alike (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 are needed very rarely -- usually they appear as a result of a programming mistake. ````smart header="Const objects can be modified" An important side effect of storing objects as references is that an object declared as `const` *can* be modified. For instance: ```js run const user = { name: "John" }; *!* user.name = "Pete"; // (*) */!* alert(user.name); // Pete ``` 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. In other words, the `const user` gives an error only if we try to set `user=...` as a whole. 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 . ```` ## Cloning and merging, Object.assign [#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? We can 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 ``` We can also use the method [Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign). The syntax is: ```js Object.assign(dest, ...sources) ``` - The first argument `dest` is a target object. - Further arguments is a list of source objects. It copies the properties of all source objects into the target `dest`, and then returns it as the result. For example, we have `user` object, let's add a couple of permissions to it: ```js run 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 } alert(user.name); // John alert(user.canView); // true alert(user.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 perform a simple object cloning: ```js run let user = { name: "John", age: 30 }; *!* let clone = Object.assign({}, user); */!* alert(clone.name); // John alert(clone.age); // 30 ``` Here it copies all properties of `user` into the empty object and returns it. There are also other methods of cloning an object, e.g. using the [spread syntax](info:rest-parameters-spread) `clone = {...user}`, covered later in the tutorial. ## Nested cloning Until now we assumed that all properties of `user` are primitive. But properties can be references to other objects. 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 `user.sizes` is an object, and will be copied by reference, so `clone` and `user` will share the same sizes: ```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 = 60; // change a property from one place alert(clone.sizes.width); // 60, get the result from the other one ``` 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. ### structuredClone The call `structuredClone(object)` clones the `object` with all nested properties. Here's how we can use it in our example: ```js run let user = { name: "John", sizes: { height: 182, width: 50 } }; *!* let clone = structuredClone(user); */!* alert( user.sizes === clone.sizes ); // false, different objects // user and clone are totally unrelated now user.sizes.width = 60; // change a property from one place alert(clone.sizes.width); // 50, not related ``` The `structuredClone` method can clone most data types, such as objects, arrays, primitive values. It also supports circular references, when an object property references the object itself (directly or via a chain or references). For instance: ```js run let user = {}; // let's create a circular reference: // user.me references the user itself user.me = user; let clone = structuredClone(user); alert(clone.me === clone); // true ``` As you can see, `clone.me` references the `clone`, not the `user`! So the circular reference was cloned correctly as well. Although, there are cases when `structuredClone` fails. For instance, when an object has a function property: ```js run // error structuredClone({ f: function() {} }); ``` Function properties aren't supported. 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). ## 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 itself. 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 `structuredClone` or use a custom cloning implementation, such as [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep).