1. Data Type Storage#
In the previous article, we mentioned that there are two major data types in JavaScript:
- Primitive types
- Reference types
Primitive type data is stored in the stack memory.
Reference type data is stored in the heap memory. Variables of reference data types are references to actual objects in the heap and are stored in the stack.
2. Shallow Copy#
Shallow copy refers to creating a new data that has an exact copy of the original data's property values.
If the property is a primitive type, the copy is the value of the primitive type. If the property is a reference type, the copy is the memory address.
In other words, shallow copy only copies one level, while deeply nested reference types share the same memory address.
Here is a simple implementation of shallow copy:
function shallowClone(obj) {
const newObj = {};
for(let prop in obj) {
if(obj.hasOwnProperty(prop)){
newObj[prop] = obj[prop];
}
}
return newObj;
}
In JavaScript, there are several ways to achieve shallow copy:
Object.assign
Array.prototype.slice()
,Array.prototype.concat()
- Copying using the spread operator
Object.assign#
var obj = {
age: 18,
nature: ['smart', 'good'],
names: {
name1: 'fx',
name2: 'xka'
},
love: function () {
console.log('fx is a great girl')
}
}
var newObj = Object.assign({}, obj);
slice()#
const arr = ["One", "Two", "Three"]
const newArr = arr.slice(0)
newArr[1] = "love";
console.log(arr) // ["One", "Two", "Three"]
console.log(newArr) // ["One", "love", "Three"]
concat()#
const arr = ["One", "Two", "Three"]
const newArr = arr.concat()
newArr[1] = "love";
console.log(arr) // ["One", "Two", "Three"]
console.log(newArr) // ["One", "love", "Three"]
Spread Operator#
const arr = ["One", "Two", "Three"]
const newArr = [...arr]
newArr[1] = "love";
console.log(arr) // ["One", "Two", "Three"]
console.log(newArr) // ["One", "love", "Three"]
3. Deep Copy#
Deep copy creates a new stack, and the two objects are completely the same but have different addresses. Modifying the properties of one object will not affect the properties of the other object.
Common methods for deep copy are:
- _.cloneDeep()
- jQuery.extend()
- JSON.stringify()
- Custom loop recursion
_.cloneDeep()#
const _ = require('lodash');
const obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
const obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false
jQuery.extend()#
const $ = require('jquery');
const obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
const obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f); // false
JSON.stringify()#
const obj2=JSON.parse(JSON.stringify(obj1));
However, this method has drawbacks as it ignores undefined
, symbol
, and function
.
const obj = {
name: 'A',
name1: undefined,
name3: function() {},
name4: Symbol('A')
}
const obj2 = JSON.parse(JSON.stringify(obj));
console.log(obj2); // {name: "A"}
Loop Recursion#
function deepClone(obj, hash = new WeakMap()) {
if (obj === null) return obj;
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
if (typeof obj !== "object") return obj;
if (hash.get(obj)) return hash.get(obj);
let cloneObj = new obj.constructor();
hash.set(obj, cloneObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}
4. Differences#
First, let's take a look at two diagrams to see the difference between shallow copy and deep copy more clearly.
From the above diagram, we can see that both shallow copy and deep copy create a new object, but the behavior of copying object properties is different.
Shallow copy only copies the pointers to the properties that point to objects, rather than copying the objects themselves. The new and old objects still share the same memory, so modifying the object properties will affect the original object.
// Shallow copy
const obj1 = {
name : 'init',
arr : [1,[2,3],4],
};
const obj3 = shallowClone(obj1); // A shallow copy method
obj3.name = "update";
obj3.arr[1] = [5,6,7]; // The new and old objects still share the same memory
console.log('obj1', obj1); // obj1 { name: 'init', arr: [ 1, [ 5, 6, 7 ], 4 ] }
console.log('obj3', obj3); // obj3 { name: 'update', arr: [ 1, [ 5, 6, 7 ], 4 ] }
On the other hand, deep copy creates a completely identical object with a different memory address. Modifying the properties of one object will not affect the properties of the other object.
// Deep copy
const obj1 = {
name : 'init',
arr : [1,[2,3],4],
};
const obj4 = deepClone(obj1); // A deep copy method
obj4.name = "update";
obj4.arr[1] = [5,6,7]; // The new object has a different memory address from the original object
console.log('obj1', obj1); // obj1 { name: 'init', arr: [ 1, [ 2, 3 ], 4 ] }
console.log('obj4', obj4); // obj4 { name: 'update', arr: [ 1, [ 5, 6, 7 ], 4 ] }
Summary#
In the case of copying reference types:
- Shallow copy only copies one level. When the property is an object, the shallow copy is a reference, and the two objects point to the same address.
- Deep copy recursively copies the nested levels. When the property is an object, the deep copy creates a new stack, and the two objects point to different addresses.