preface
Each case in this article is written and verified by myself. It is recommended that you execute the case in your browser when reading this article to help you understand it better.
JavaScript series: Advanced JavaScript
Variable storage type
To understand shallow-copy, familiarize yourself with variable storage types, which fall into basic data types (value types) and reference data types (complex data types). Values for basic data types are stored directly in stack memory, whereas stack memory for reference data types holds memory addresses and values are stored in heap memory.
Variable storage type | value | Address values | example |
---|---|---|---|
Basic data types | Stored in theThe stack In the |
String, bool, number, undefined, null, symbol(ES6 new) | |
Reference data type | Stored in theThe heap In the |
Stored in theThe stack In the |
Array, object, function, re |
Basic data types
Data that stores values directly on the stack. String, bool, number, undefined, null, symbol(new in ES6)
-
Typeof () : null; typeof() : null; Basic data types store values and have no functions to call, such as null.tostring (), which returns an error
-
2, null and undefined
null
The: js keyword is a special object value that representsA null value
, typeof operation isobject
undefined
: indicates a predefined global variableundefined
, typeof operation isundefined
-
Symbol is not a constructor.
Reference data type
The reference address of the object is stored in the stack, and the data of the object is stored in the heap. Groups, objects, functions, re
Reference type typeof operation, output object
Methods for detecting data types
- 1.typeof
Typeof is an operator that detects data types, and the output string is the corresponding type. Has the following limitations 1) |
---|
1)typeOf(null) The output isobject , butnull Are basic data types |
2) Can’t distinguish the specific type of object, such astypeof([1]) andtypeof({1}) The output is zeroobject |
-
2.instanceOf
Checks whether an instance belongs to a class
-
3.constructor
Gets the constructor for the current instance, as described in Is the constructor property read-only?
-
4.Object.prototype.toString.call
Get information about the class to which the current instance belongs. See Changing this with Call for type determination
The difference between assignment, shallow copy, and deep copy
From whether the generated new object points to the same object as the original data, and whether changing the new object will cause the original data to change the row comparison (according to the classification of the object’s first level attribute is the base data type and reference data type).
Operation type | Points to the same object as the original data | The first layer of attributes is the primitive type | The first layer of attributes is the reference type |
---|---|---|---|
The assignment | is |
changewill The original data is changed |
changewill The original data is changed |
Shallow copy | no |
changeDon't The original data is changed |
changewill The original data is changed |
Deep copy | no |
changeDon't The original data is changed |
changeDon't The original data is changed |
The assignment
The symbol = is the assignment operation. There are two types of assignment to basic data types and reference data types
- 1. Basic data type assignment
Assignment operations for basic data types are value references and have no effect on each other
var a = Valley Floor Dragon;
var b = a; // Assign a to b
a = 'World beaters'; // Change the value of a to 'invincible'
console.log(b); // Print the value of b, still 'bottom dragon'
Copy the code
- 2. Reference data type assignment
An assignment of a reference data type is an address reference, where two variables refer to the same address and affect each other
var a = {
name: Valley Floor Dragon
};
var b = a; // Assign a to b
a.name = 'World beaters'; // Change the value of a.name to 'invincible'
console.log(b.name); // Print the value of b.name to become 'invincible'
Copy the code
During development, we sometimes don’t want two variables in an assignment operation referencing a data type to affect each other, so shallow and deep copies are required
Shallow copy
Shallow copy copies only the first layer properties of the original object. That is, copy the data in object A, but do not copy the child objects in object A.
If the property is a primitive data type, the value of the primitive type is copied. If the attribute is a reference data type, the memory address of the reference type is copied
How to implement a shallow copy?
For a more intuitive understanding of shallow copy and its characteristics, let’s implement a shallow copy function manually
// Shallow copy obj
function shallowCopy(obj) {
if (typeof obj === 'object'&& obj ! = =null) {
let copy = Array.isArray(obj) ? [] : {};
// Iterate over the old object obj and assign the first level property to the new object
for (var p in obj) {
copy[p] = obj[p]
}
// The new object returned is the shallow-copied object
return copy
} else {
// If it is a basic type, return it directly
return obj
}
}
Copy the code
As you can see, the shallow copy only copies the first level properties of the original data. Now verify the contents of the previous table:
- If the first level property is a primitive data type, change the new object
Don't
The original data is changed; - If the first level property is a reference data type, change the new object
will
The original data is changed
// The raw data is an object
var obj = {
color: 'red'.person: {
name: Valley Floor Dragon.age: 28,}}/ / shallow copy
var copy = shallowCopy(obj);
// Change the original data layer 1 attribute color (base data type)
copy.color = 'yellow';
// Change the first layer attribute of the original data person (reference data type)
copy.person.name = 'World beaters';
// The original data color is still "red", but the name will be changed to "invincible".
console.log(obj);
Copy the code
The result is as follows:
If you replace the original data with an array, the effect is the same, as follows
// The original data is an array
var obj = [
'red',
{
name: Valley Floor Dragon.age: 28,},]/ / shallow copy
var copy = shallowCopy(obj);
// Change the original data layer 1 attribute color (base data type)
copy[0] = 'yellow';
// Change the first layer attribute of the original data person (reference data type)
copy[1].name = 'World beaters';
// The original data color is still "red", but the name will be changed to "invincible".
console.log(obj);
Copy the code
The result is as follows:
What if the source data is a primitive data type?
- Before speaking
The assignment
When, mentionShallow copy
andDeep copy
It was introduced to solve the problem of reference data type (complex data type) assignment. So if the source data is a primitive data type, assignment is usually all that is required. If you use the shallow copy function implemented aboveshallowCopy()
To copy the base data type and return the original data directly.
What are the common shallow copies?
In practical development, we rarely need to write a shallow copy function, here are a few commonly used shallow copy
1. The Object Object. The assign ()
In ES6, object. assign(target,… Sources) to implement shallow copy, the first parameter is the target object, the following parameters… Sources is the source object. So let’s reframe the previous case
// The raw data is an object
var obj = {
color: 'red'.person: {
name: Valley Floor Dragon.age: 28,}}// Use object.assign () to implement shallow copy
var copy = Object.assign({},obj);
// Change the original data layer 1 attribute color (base data type)
copy.color = 'yellow';
// Change the first layer attribute of the original data person (reference data type)
copy.person.name = 'World beaters';
// The original data color is still "red", but the name will be changed to "invincible".
console.log(obj);
Copy the code
The result is the same as shallowCopy()
Note: Object.assign() is generally used for shallow copies of objects, which are treated as objects when dealing with arrays.
// object. assign treats arrays as objects with attributes 0, 1, and 2
// Attribute 0 7 of the source array overrides attribute 0 1 of the destination array, and so on
Object.assign([1.2.3], [7.8]); / /,8,3 [7]
Object.assign([1.2], [6.7.8]); / / / June,
Copy the code
2. Array. An Array concat ()
For shallow copies of arrays, you can use array.concat (), which is also a common way to merge arrays. Let’s revise the previous case
// The original data is an array
var obj = [
'red',
{
name: Valley Floor Dragon.age: 28,},]// Use [].concat() to implement shallow copies
var copy = [].concat(obj);
// Change the original data layer 1 attribute color (base data type)
copy[0] = 'yellow';
// Change the first layer attribute of the original data person (reference data type)
copy[1].name = 'World beaters';
// The original data color is still "red", but the name will be changed to "invincible".
console.log(obj);
Copy the code
The result is the same as shallowCopy()
3. Extension operator {… obj }
Use the extension operator {… Obj} can make a shallow copy of an array and an object. Let’s use the case where the original data is an object, or you can try replacing the original data with an array yourself.
// The raw data is an object
var obj = {
color: 'red'.person: {
name: Valley Floor Dragon.age: 28,}}// Use the extension operator {... Obj} implements shallow copy
varcopy = {... obj};// Change the original data layer 1 attribute color (base data type)
copy.color = 'yellow';
// Change the first layer attribute of the original data person (reference data type)
copy.person.name = 'World beaters';
// The original data color is still "red", but the name will be changed to "invincible".
console.log(obj);
Copy the code
The execution effect is the same as other shallow copy methods
Deep copy
A shallow copy only copies the first layer attributes. There is a problem: if the first layer attributes are reference types and addresses are copied, the shallow copy will cause the original data to be modified. So how do you solve this problem? You need a deep copy.
A deep copy is a complete copy of the object in memory, creating a new memory space in the heap, completely independent of the original object. Modifying the new object does not affect the original object.
How to implement a deep copy?
Shallow copy + recursion
A shallow copy copies only the first layer of the property, and a deep copy copies all the way to the last layer (until the property is of a basic type).
// Recursive shallow copy
function recursiveShallowCopy(obj) {
var copy = Array.isArray(obj) ? [] : {};
for (let p in obj) {
if (typeof obj[p] === 'object') {
// Object type, continue recursive shallow copy
copy[p] = recursiveShallowCopy(obj[p]);
} else{ copy[p] = obj[p]; }}return copy;
}
/ / copy
function deepCopy(obj) {
if (typeof obj === 'object'&& obj ! = =null) {
// If it is a reference type, make a recursive shallow copy
return recursiveShallowCopy(obj);
} else {
// If it is a basic type, return it directly
returnobj; }}Copy the code
We now verify the previous table with the deep-copy function created above: deepCopy()
- In deep copy, modifying a new object does not affect the original data, regardless of whether the first-level attributes are of primitive or reference type.
// The raw data is an object
var obj = {
color: 'red'.person: {
name: Valley Floor Dragon,}}// use deepCopy() to implement deepCopy
var copy = deepCopy(obj);
// Change the original data layer 1 attribute color (base data type)
copy.color = 'yellow';
// Change the first layer attribute of the original data person (reference data type)
copy.person.name = 'World beaters';
// Original data color is still "red", name is still "the bottom of the dragon"
console.log(obj);
Copy the code
Execute the above case and print the following results:
The same thing happens to arrays, so I’m not going to write it again.
2.JSON.parse(JSON.stringify())
Json.stringify () serializes the original object into a JSON string, and json.parse () generates a new object.
This is the more commonly used deep copy mode. Parse (json.stringify ()) to implement deep copy validation:
// The raw data is an object
var obj = {
color: 'red'.person: {
name: Valley Floor Dragon,}}Parse (json.stringify ()) instead for deep copy
var copy = JSON.parse(JSON.stringify(obj));
// Change the original data layer 1 attribute color (base data type)
copy.color = 'yellow';
// Change the first layer attribute of the original data person (reference data type)
copy.person.name = 'World beaters';
// Original data color is still "red", name is still "the bottom of the dragon"
console.log(obj);
Copy the code
The result is the same as deepCopy() with recursion + shallow copy
Precautions for deep copy
Using json.parse (json.stringify () to implement deep copy has some drawbacks:
1. If the original object contains undefined, Symbol, or function, the key value will be lost
2. If the original object has a re, it will be converted to an empty object {}
3. If the original object contains Date, it is converted to a string
4. Discard the constructor of the original Object and convert it to Object
5. If a circular reference exists in the object, it cannot be handled correctly
Let’s verify the first three defects:
// The raw data is an object
var obj = {
color: 'red'.person: {
name: null.age: function(){}, / / has been lost
country: undefined./ / has been lost
love: Symbol(), / / has been lost
time: new Date(),// Convert to a string
height: / [1-9] [0-9]? /.// Convert to an empty object {}}},Parse (json.stringify () to implement deep copy
var copy = JSON.parse(JSON.stringify(obj));
console.log(copy)
Copy the code
After execution, the original object is foundAttributes age (function), country (undefined), love (Symbol) are missing
.Time (Date) is converted to a string
.Height (re) is converted to an empty object {}
. The print result is as follows
Parse (json.stringify ()) implements deep copy, discarding the constructor of the original Object and converting it to Object
function Person() {
this.name = Valley Floor Dragon
}
var obj = {
person: new Person()
}
console.log('Original object:')
console.log(obj)
var copy = JSON.parse(JSON.stringify(obj))
console.log('New object:')
console.log(copy)
Copy the code
After a deep copy, whatever the original constructor of this Object was, after a deep copy, it will become Object. The result is as follows:
Note:
useJSON.parse(JSON.stringify()
Implementing the first 4 defects of deep copy can be passedRecursion + shallow copy
The deep copy mode is solved, howeverRecursion + shallow copy
Does not solve the problem of circular references in point 5. Write a wenjiang at the back
reference
- Parse the differences between assignment, shallow copy, and deep copy
- Deep understanding of javascript deep copy, shallow copy, and circular references
conclusion
It took me two days to write. If you like this article, please give it a thumbs up
You can also follow my public account “flying dragon at the bottom of valley” ~