
Copying objects with Shallow copy
Now that we went over shallow and deep copies in part 1, let’s go over shallow copying in JavaScript. There are 3 ways to create shallow copies in JavaScript:
- Iteration using for key in an object
- Spread operator
- Object.assign()
1. Iteration using for key in an object
In order to copy the items of an object into another object we can iterate through the key : value pairs of an object and assign them to the second object. Let’s copy a course object’s key : value pairs into a target object using iteration:
let console1 ={ one: 'Nintendo Switch', two: 'XBox', three: 'PlayStation', four: 'Gameboy' }; let console2 = {}; for(key in console1){ //console.log(console1[key]) console2[key] = console1[key]; } console.log(console2);
The following will be logged to the console:
Object { 227 one: "Nintendo Switch", two: "XBox", three: "PlayStation", four: "Gameboy" }
Looping will create a shallow copy by copying the enumerable properties from console1
into console2. Let’s now change the value of one of the properties in the console2 object which is a copy of the console1 object:
console2.one = 'Sega';
We have assigned the value of the one property to ‘Sega’ which replaces the value that
was copied over for this property from the console1 object (‘Nintendo Switch’).
Do you think that console1 will reflect this change? Let’s check:
console.log(console1); [object Object] { four: "Gameboy", one: "PlayStation", three: "PlayStation", two: "XBox" }
The value of one has not changed in the source object console1 since primitive values
are copied by value. Whereas if a field is of a reference/object type then the reference/memory address is copied over to the newly created target object. So changing the value at one location will not change the value at another location for a property whose value is a primitive type.
2. Spread operator
The spread operator (three dots) introduced in ES6 will copy over the enumerable
properties of an iterable source object to the target object.
Syntax: {…}
Let’s have a look at console1 which is an object with four key : value pairs:
let console1 ={ 228 one: 'Nintendo Switch', two: 'XBox', three: 'PlayStation', four: 'Gameboy' }
We will now use the spread operator to make a shallow copy and copy over all the primitive string values over from the target console1 object to the source console2 object
let console2 = {...console1};
Logging console2 to the console we see that all the values are duplicated and are now
inside the target console2 object:
Object { one: "Nintendo Switch", two: "XBox", three: "PlayStation", four: "Gameboy" }
The spread operator can also be used to merge two iterable objects. For example the two
object literals in the following example:
let console1 ={ one: 'Nintendo Switch', two: 'XBox', three: 'PlayStation', four: 'Gameboy' } let console2 = { five: 'Sega Saturn', six: 'GameCube', seven: 'Wii', 229 eight: 'Atari 2600' }
Using the spread operator will merge them into a single object reference by let
consoles:
let consoles = {...console1, ...console2};
The consoles object now contains the key : value pairs from both the console1 and
console2 objects:
Object { eight: "Atari 2600", five: "Sega Saturn", four: "Gameboy", one: "Nintendo Switch", seven: "Wii", six: "GameCube", three: "PlayStation", two: "XBox" }
Duplicate keys are overwritten so if we have two keys named 1, then the latter value will
prevail:
let console1 ={ one: 'Nintendo Switch', two: 'XBox', }; let console2 = { one: 'Sega Saturn', three: 'GameCube', }; let consoles = {...console1, ...console2}; console.log(consoles);
In the preceding object literals, the key 1 is duplicated and the value is over-written by the value of the 2nd number 1 key in the console2 object. The console2 object now has the following key : value pairs:
Object { one: "Sega Saturn", three: "GameCube", two: "XBox" }
3. Object.assign
The ES6 Object.assign method will also make a shallow copy by copying enumerable
properties from a target object to a source object. This method will take two arguments:
1. Target object
2. Source object
Take for example an object called box whose own properties will be copied over into a
source object:
let box = { height: 100, width: 100 };
Using the Object.assign() method we can assign the properties from the box object into
an empty object denoted by the empty braces {}. The empty object is referenced by let box_copy:
let box_copy = Object.assign({}, box);
Therefore, when we log box_copy to the console the following is returned:
Object { height: 100, width: 100 }
Therefore, properties and their values have been shallow copied.
We can use Object.assign() method to merge two objects in the following way:
let box = { height: 100, width: 100 } let box2 = { border: 20, color: 'green' }
We have two boxes called box1 and box2, we will be merging the properties of the two
boxes using Object.assign():
let merged_box = Object.assign({}, box, box2);
The method takes 3 arguments:1.
- An empty object {}
- Variable box
- Variable box2
The method will assign the key : value pairs of objects box and box2 to an empty object
referenced by let merged_box. Therefore, merged box now contains the following key:value pairs:
Object { border: 20, color: "green", height: 100, width: 100 }
If the source objects have the same property name, then the value of the property within
the latter object will overwrite the former object.
Primitive values are copied by value whereas for reference values, the memory address is copied over. And in a shallow copy both the source and target objects share a
reference/memory address. To understand this, let’s modify the box object so that the key : value pairs are now:
let box = { height: 100, width: 100, colors: { one: 'peach', two: 'coral', three: 'orange' } };
The box object now has a property object called colors which is an object with three
key: value pairs. Let’s create a shallow copy using the Object.assign() method as
before:
let box_copy = Object.assign({}, box);
This newly created object has all the key : value pairs copied over from the box object:
[object Object] { colors: [object Object] { one: "peach", three: "orange", two: "coral" }, height: 100, width: 100 }
Let’s change the value of the height property in box_copy to 200 and check whether
the height property in the source object box, is changed:
box_copy.height = 200; console.log(box);
The console.log statement will log the following for the source box object:
[object Object] { colors: [object Object] { one: "peach", three: "orange", two: "coral" }, height: 100, width: 100 }
The height property in the original box object remains unchanged, even though we
assigned a value of 200 in the copy that we created. This is because primitive types are
copied by value and not by reference. So changing the value of a primitive type in the target object does not affect the source object.
To check whether the target and source objects share a memory address we will now
change the property called one of the colors property which is a nested object inside the
box_copy object:
box_copy.colors.one = 'red';
Since memory addresses are shared and objects are copied by reference, let’s log the box
object to the console and we should see that the value of property one of the nested
colors object is now red instead of peach:
console.log(box);
[object Object] { colors: [object Object] { one: "red", three: "orange", two: "coral" }, height: 100, width: 100 }
And we see that in both box and box_copy the value peach is replaced with red