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 stackIn the String, bool, number, undefined, null, symbol(ES6 new)
Reference data type Stored in theThe heapIn the Stored in theThe stackIn 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

    • nullThe: 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, butnullAre 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 changewillThe original data is changed changewillThe original data is changed
Shallow copy no changeDon'tThe original data is changed changewillThe original data is changed
Deep copy no changeDon'tThe original data is changed changeDon'tThe 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 objectDon'tThe original data is changed;
  • If the first level property is a reference data type, change the new objectwillThe 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 speakingThe assignmentWhen, mentionShallow copyandDeep copyIt 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 copyThe deep copy mode is solved, howeverRecursion + shallow copyDoes 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” ~