Use pure JavaScript to get a perfect deep copy

Achieve almost perfect deep copy of about 20 lines

Pre-knowledge

Data types in Javascript can be divided into primitive value types and reference value types. When we perform data operations, they will have some differences.

let primitiveValue = 1;
const copyPrimitiveValue = primitiveValue;
primitiveValue = 2;
console.log('primitiveValue: ', primitiveValue); // 2
console.log('copyPrimitiveValue: ', copyPrimitiveValue); // 1
const referenceValue = { value: 1 };
const copyReferenceValue = referenceValue;
referenceValue.value = 2;
console.log('referenceValue: ', referenceValue); // { value: 2 }
console.log('copyReferenceValue: ', copyReferenceValue); // { value: 2 }

Looking at the above code, it is not difficult to find that when the original value type changes, its copied variable will not have any effect; but when the reference value type changes, its copied variable also changes. To explain this, we must first understand how data is stored in Javascript.

As mentioned above, data types can be divided into two types. Primitive value types include undefined, null, number, string, boolean, symbol, and bigint; reference value types include array objects derived from Object, function objects, and so on. Primitive value types are stored directly on the stack, while reference value types are stored on the heap, and there is an address stored on the stack pointing to it.

Javascript variable storage diagram

In this way, we can explain the above phenomenon. For the original value type, a copy is directly copied, and for the reference value type, an address is copied, which causes both referenceValue and copyReferenceValue to point to the same piece of data on the heap, so by Both of these addresses can change the same data on the heap.

Shallow copy

Shallow copy means that only one layer of the object is copied, and the deep layer of the object directly copies an address. There are many native methods in Javascript that are shallow copy. For example using object.assign or the spread operator.

const target = {};
const source = { a: { b: 1 } };
Object.assign(target, source);
source.a.b = 2;
console.log(source); // { a: { b: 2 } };
console.log(target); // { a: { b: 2 } };
// Same effect as Object.assign
const target1 = { ...source };

Deep copy

A deep copy means cloning two identical objects, but without any connection to each other.

  1. JSON.stringify
const source = { a: { b: 1 } };
const target = JSON.parse(JSON.stringify(source));
source.a.b = 2;
console.log(source); // { a: { b: 2 } };
console.log(target); // { a: { b: 1 } };

Well, it seems that JSON.stringify can achieve deep copying, but it has some defects. For example, it cannot copy functions, undefined, Date , cannot copy non-enumerable properties, cannot copy circularly referenced objects, and so on. You can check out the detailed description on MDN.

2. Almost perfect deep copy

https://medium.com/media/0e8f1c8e2608d286fd9c106d184ff13d/href

The above code is the final product, let me explain how it came about.

  1. First use WeakMap as hash table to solve the circular reference problem and effectively prevent memory leaks.
  2. For the special types Date and RegExp, a new instance is directly generated and returned.
  3. Use Object.getOwnPropertyDescriptors to get all property descriptions of the current object, and use Object.getPrototypeOf to get the prototype of the current object. Passing these two items as arguments to Object.create creates a new identical object.
  4. Use Reflect.ownKeys to iterate over all properties of the current object, including non-enumerable properties and Symbol properties, as well as normal properties. In this way, you can keep copying the deep value to the current new object in the loop, and process the whole process in the recursive call.
  5. In the loop judgment, except that the function is directly assigned, the others are re-copied by recursion.

Next, we can use the test code to verify.

const symbolKey = Symbol('symbolKey');
const originValue = {
num: 0,
str: '',
boolean: true,
unf: void 0,
nul: null,
obj: { name: 'object', id: 1 },
arr: [0, 1, 2],
func() {
console.log('function');
},
date: new Date(0),
reg: new RegExp('/regexp/ig'),
[symbolKey]: 'symbol',
};
Object.defineProperty(originValue, 'innumerable', {
enumerable: false,
value: 'innumerable',
});
// Create circular reference
originValue.loop = originValue;
// Deep Copy
const clonedValue = deepClone(originValue);
// Change original value
originValue.arr.push(3);
originValue.obj.name = 'newObject';
// Remove circular reference
originValue.loop = '';
originValue[symbolKey] = 'newSymbol';
console.log('originValue: ', originValue);
console.log('clonedValue: ', clonedValue);
Compare Photo

It’s pretty cool that we have an almost perfect deep copy in Javascript.

If you also love front-end, please follow me, I will often publish some good articles on front-end.


Use pure JavaScript to get a perfect deep copy was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Level Up Coding - Medium and was authored by islizeqiang

Achieve almost perfect deep copy of about 20 lines

Pre-knowledge

Data types in Javascript can be divided into primitive value types and reference value types. When we perform data operations, they will have some differences.

let primitiveValue = 1;
const copyPrimitiveValue = primitiveValue;
primitiveValue = 2;
console.log('primitiveValue: ', primitiveValue); // 2
console.log('copyPrimitiveValue: ', copyPrimitiveValue); // 1
const referenceValue = { value: 1 };
const copyReferenceValue = referenceValue;
referenceValue.value = 2;
console.log('referenceValue: ', referenceValue); // { value: 2 }
console.log('copyReferenceValue: ', copyReferenceValue); // { value: 2 }

Looking at the above code, it is not difficult to find that when the original value type changes, its copied variable will not have any effect; but when the reference value type changes, its copied variable also changes. To explain this, we must first understand how data is stored in Javascript.

As mentioned above, data types can be divided into two types. Primitive value types include undefined, null, number, string, boolean, symbol, and bigint; reference value types include array objects derived from Object, function objects, and so on. Primitive value types are stored directly on the stack, while reference value types are stored on the heap, and there is an address stored on the stack pointing to it.

Javascript variable storage diagram

In this way, we can explain the above phenomenon. For the original value type, a copy is directly copied, and for the reference value type, an address is copied, which causes both referenceValue and copyReferenceValue to point to the same piece of data on the heap, so by Both of these addresses can change the same data on the heap.

Shallow copy

Shallow copy means that only one layer of the object is copied, and the deep layer of the object directly copies an address. There are many native methods in Javascript that are shallow copy. For example using object.assign or the spread operator.

const target = {};
const source = { a: { b: 1 } };
Object.assign(target, source);
source.a.b = 2;
console.log(source); // { a: { b: 2 } };
console.log(target); // { a: { b: 2 } };
// Same effect as Object.assign
const target1 = { ...source };

Deep copy

A deep copy means cloning two identical objects, but without any connection to each other.

  1. JSON.stringify
const source = { a: { b: 1 } };
const target = JSON.parse(JSON.stringify(source));
source.a.b = 2;
console.log(source); // { a: { b: 2 } };
console.log(target); // { a: { b: 1 } };

Well, it seems that JSON.stringify can achieve deep copying, but it has some defects. For example, it cannot copy functions, undefined, Date , cannot copy non-enumerable properties, cannot copy circularly referenced objects, and so on. You can check out the detailed description on MDN.

2. Almost perfect deep copy

The above code is the final product, let me explain how it came about.

  1. First use WeakMap as hash table to solve the circular reference problem and effectively prevent memory leaks.
  2. For the special types Date and RegExp, a new instance is directly generated and returned.
  3. Use Object.getOwnPropertyDescriptors to get all property descriptions of the current object, and use Object.getPrototypeOf to get the prototype of the current object. Passing these two items as arguments to Object.create creates a new identical object.
  4. Use Reflect.ownKeys to iterate over all properties of the current object, including non-enumerable properties and Symbol properties, as well as normal properties. In this way, you can keep copying the deep value to the current new object in the loop, and process the whole process in the recursive call.
  5. In the loop judgment, except that the function is directly assigned, the others are re-copied by recursion.

Next, we can use the test code to verify.

const symbolKey = Symbol('symbolKey');
const originValue = {
num: 0,
str: '',
boolean: true,
unf: void 0,
nul: null,
obj: { name: 'object', id: 1 },
arr: [0, 1, 2],
func() {
console.log('function');
},
date: new Date(0),
reg: new RegExp('/regexp/ig'),
[symbolKey]: 'symbol',
};
Object.defineProperty(originValue, 'innumerable', {
enumerable: false,
value: 'innumerable',
});
// Create circular reference
originValue.loop = originValue;
// Deep Copy
const clonedValue = deepClone(originValue);
// Change original value
originValue.arr.push(3);
originValue.obj.name = 'newObject';
// Remove circular reference
originValue.loop = '';
originValue[symbolKey] = 'newSymbol';
console.log('originValue: ', originValue);
console.log('clonedValue: ', clonedValue);
Compare Photo

It’s pretty cool that we have an almost perfect deep copy in Javascript.

If you also love front-end, please follow me, I will often publish some good articles on front-end.


Use pure JavaScript to get a perfect deep copy was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Level Up Coding - Medium and was authored by islizeqiang


Print Share Comment Cite Upload Translate Updates
APA

islizeqiang | Sciencx (2022-02-16T04:05:15+00:00) Use pure JavaScript to get a perfect deep copy. Retrieved from https://www.scien.cx/2022/02/16/use-pure-javascript-to-get-a-perfect-deep-copy/

MLA
" » Use pure JavaScript to get a perfect deep copy." islizeqiang | Sciencx - Wednesday February 16, 2022, https://www.scien.cx/2022/02/16/use-pure-javascript-to-get-a-perfect-deep-copy/
HARVARD
islizeqiang | Sciencx Wednesday February 16, 2022 » Use pure JavaScript to get a perfect deep copy., viewed ,<https://www.scien.cx/2022/02/16/use-pure-javascript-to-get-a-perfect-deep-copy/>
VANCOUVER
islizeqiang | Sciencx - » Use pure JavaScript to get a perfect deep copy. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/02/16/use-pure-javascript-to-get-a-perfect-deep-copy/
CHICAGO
" » Use pure JavaScript to get a perfect deep copy." islizeqiang | Sciencx - Accessed . https://www.scien.cx/2022/02/16/use-pure-javascript-to-get-a-perfect-deep-copy/
IEEE
" » Use pure JavaScript to get a perfect deep copy." islizeqiang | Sciencx [Online]. Available: https://www.scien.cx/2022/02/16/use-pure-javascript-to-get-a-perfect-deep-copy/. [Accessed: ]
rf:citation
» Use pure JavaScript to get a perfect deep copy | islizeqiang | Sciencx | https://www.scien.cx/2022/02/16/use-pure-javascript-to-get-a-perfect-deep-copy/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.