preface

Why write copy this article? A colleague mentioned copying the other day and said assignment is a shallow copy. Another colleague said assignment is not the same as shallow copy. I have some doubt, so I went to the MDN search copy related content, found no copy about the essence of the concept, there is no way to only through the practice, at the same time to see some predecessors article summarizes this article about copy of the content, this article also belong to the public, “programmers growth refers to the north” learning route “JS will know will be” content.

Koala is dedicated to sharing the complete Node.js technology stack, from JavaScript to Node.js, to back-end database. Wish you become an excellent senior Node.js engineer. [Programmer growth refers to north] Author, Github blog open source project github.com/koala-codin…

The relationship between data types and stacks

Base and reference types

  • Basic types: undefined, null, Boolean, String, Number, Symbol

  • Reference types: Object, Array, the Date, the Function, the RegExp, etc

storage

  • Basic type: Values of the basic type occupy a fixed size in memory and are stored inStack memoryIn (not includedclosureIn)

  • Reference type: The value of a reference type is an object, stored inHeap memoryIn the. The stack memory stores the variable identifier of the object and the storage address of the object in the heap memory (reference). The reference data type stores a pointer in the stack to the starting address of the entity in the heap. When the interpreter looks for a reference value, it first retrieves its address in the stack and then retrieves the entity from the heap.

Note:

  1. closureVariables in are not stored in stack memory, but in heap memory. That’s kind of nice, ifclosureThe variable is saved inStack memoryAs the function in the outer layer is destroyed from the call stack, the variables are definitely destroyed, but if stored in heap memory, the in-memory function can still access the variables in the destroyed function in the outer layer. Take a look at the corresponding code to understand:
function A() {
  let a = 'koala'
  function B() {
      console.log(a)
  }
  return B
}
Copy the code
  1. The shallow and deep copies discussed in this article are for reference types, not for underlying types.

The assignment operation

Base data type replication

Look at a piece of code

let a ='koala';
let b = a;
b='Programmer growth is north';console.log(a); // koala
Copy the code

Basic data type replication diagram:

Conclusion: When the data in the stack memory changes, the system will automatically assign a new value to the new variable in the stack memory, and the two variables are independent of each other.

Reference data type replication

Look at a piece of code

let a = {x:'kaola'.y:'kaola1'}
let b = a;
b.x = 'Programmer growth is north';
console.log(a.x); // Programmer growth refers to north
Copy the code

Reference data type replication diagram:

conclusionA copy of the reference type, which also allocates a new value to the new variable B, is reported in the stack memory, but the specific value of this variable is not in the stack, the stack is only an address pointer. Both variable address Pointers are the same, pointing to objects in heap memory, so when B.x changes, A.x also changes.

Shallow copy

Shallow copy definition:

A function called array.prototype. slice can implement a shallow copy of the original Array. For the official conclusion, let’s verify it with two pieces of code and summarize the definition of shallow copy.

  • First code:
var a = [ 1.3.5, { x: 1}];var b = Array.prototype.slice.call(a);
b[0] = 2;
console.log(a); // [ 1, 3, 5, { x: 1 } ];
console.log(b); // [ 2, 3, 5, { x: 1 } ];
Copy the code

As can be seen from the output result, after shallow copy, array A [0] does not change with the change of array B [0], indicating that a and B reference addresses in stack memory are not the same.

  • Second code
var a = [ 1.3.5, { x: 1}];var b = Array.prototype.slice.call(a);
b[3].x = 2;
console.log(a); // [ 1, 3, 5, { x: 2 } ];
console.log(b); // [ 1, 3, 5, { x: 2 } ];
Copy the code

From the output, it can be seen that after the shallow copy, the properties of the objects in the array will change according to the modification, indicating that the object property reference of the existing object is copied during the shallow copy.

  • Shallow copy definition

Analyze the shallow copy definition with this official Slice shallow copy function:

The new object copies the values and references of non-object attributes of the existing object. If the term is not understood in another way, a new object directly copies a reference to an existing object’s object properties, that is, a shallow copy.

Shallow-copy instance

Object.assign

  • Grammar:

Syntax: Object.assign(target,… sources)

The method that copies objects in ES6 takes the first parameter as the target to be copied and the remaining parameters as the source object to be copied (which can be multiple).

  • For example:
let target = {};
let source = {a:'koala'.b: {name:'Programmer growth is north'}};
Object.assign(target ,source);
console.log(target); // {a: 'koala', b: {name: 'koala'}}
source.a = 'smallKoala';
source.b.name = 'Programmer growth means north.'
console.log(source); // {a: 'smallKoala', b: {name: 'smallKoala'}}
console.log(target); // {a: 'koala', b: {name: 'koala'}}
Copy the code

Object.assign is a shallow copy. It creates a new Object in the root attribute (the first level of the Object), but copies the same memory address if the attribute value is an Object.

  • Object.assign Precautions
  1. Copy only the source object’s own properties (no inherited properties)
  2. It does not copy the object’s non-enumerable properties
  3. undefinedandnullThey can’t be turned into objects, they can’t beObject.assignParameter, but can be used as a source object
Object.assign(undefined) / / an error
Object.assign(null) / / an error

let obj = {a: 1};
Object.assign(obj, undefined) === obj // true
Object.assign(obj, null) === obj // true
Copy the code
  1. Properties, SymbolValue, which can be copied by Object.assign.

Array.prototype.slice

This function was analyzed when the shallow copy concept was defined, see above.

Array.prototype.concat

  • grammar

var new_array = old_array.concat(value1[, value2[, …[, valueN]]])

Parameters: Concatenate arrays and/or values into new arrays

  • For example
let array = [{a: 1}, {b: 2}];
let array1 = [{c: 3}, {d: 4}];
let array2=array.concat(array1);
array1[0].c=123;
console.log(array2);// [ { a: 1 }, { b: 2 }, { c: 123 }, { d: 4 } ]
console.log(array1);// [ { c: 123 }, { d: 4 } ]
Copy the code

Array.prototype.concat is also a shallow copy, just creating a new object in the root property (the first level of the object), but copying the same memory address if the property value is an object.

. Extended operator

  • grammar

var cloneObj = { … obj };

  • For example
let obj = {a:1.b: {c:1}}
letobj2 = {... obj}; obj.a=2;
console.log(obj); //{a:2,b:{c:1}}
console.log(obj2); //{a:1,b:{c:1}}

obj.b.c = 2;
console.log(obj); //{a:2,b:{c:2}}
console.log(obj2); //{a:1,b:{c:2}}
Copy the code

The extension operator is also a shallow copy. You cannot copy an attribute whose value is an object into two different objects, but it is also a convenience if the attribute is of primitive type.

Note: The above 4 shallow copy methods do not change the original array, only return a shallow copy of the original array elements of a new array.

Implement a shallow copy yourself

How it works: New objects copy the values and references of non-object attributes of existing objects, that is, object attributes are not copied to memory.

  • Implementation code:
function cloneShallow(source) {
    var target = {};
    for (var key in source) {
        if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; }}return target;
}
Copy the code
  • HasOwnProperty = hasOwnProperty = hasOwnProperty = hasOwnProperty

for in

for… The IN statement iterates through an object’s own, inherited, enumerable, non-symbol properties in any order. For each different attribute, the statement is executed.

hasOwnProperty

Grammar: obj. HasOwnProperty (prop)

Prop is the name of the property string or Symbol to be tested

This function returns a Boolean value, and all objects that inherit from Object inherit to the hasOwnProperty method. Unlike the in operator, this function ignores properties inherited from the prototype chain as well as its own properties.

Deep copy operation

The assignment operation and the shallow copy operation, if you can already think of what is a deep copy, let’s go straight to the definition of deep copy.

Deep copy definition

Deep copy creates a new area of heap memory to store the new object. The new object does not share memory with the original object, and modification of the new object will not change to the original object.

Deep-copy instance

JSON.parse(JSON.stringify())

Json.stringify () is a common deep-copy method used in front-end development. The principle is to serialize an object into a JSON string, convert the contents of the object into a string and store it on disk, and then convert the JSON string into a new object using json.parse () deserialization

  • For example:
let arr = [1.3, {
    username: ' koala'
}];
let arr4 = JSON.parse(JSON.stringify(arr));
arr4[2].username = 'smallKoala'; 
console.log(arr4);// [ 1, 3, { username: 'smallKoala' } ]
console.log(arr);// [ 1, 3, { username: ' koala' } ]
Copy the code

The implementation of deep copy, when changing the value of the object in the array, the contents of the original array does not change. Json.stringify () can implement deep copy, but has some drawbacks such as not being able to handle functions.

  • Json.stringify () implements deep copy notes
  1. If the value of the copied object has a function,undefined,symbol, the key-value pair will disappear in the JSON string serialized by json.stringify ()
  2. Cannot copy non-enumerable properties, cannot copy the prototype chain of an object
  3. Copying the Date reference type becomes a string
  4. Copying the RegExp reference type becomes an empty object
  5. Object containing NaN, Infinity, and -infinity becomes NULL
  6. Looping applications that cannot copy objects (i.e. obj[key] = obj)

Implement a simple deep copy yourself

Deep copy, the main idea is recursion, iterating through objects and arrays until they are all basic data types, and then copying, that is, deep copy. Implementation code:

    // Define a function that detects data types
    function isObject(obj) {
	    return typeof obj === 'object'&& obj ! =null;
    }
   function cloneDeep(source) {

    if(! isObject(source))return source; // Non-objects return themselves
      
    var target = Array.isArray(source) ? [] : {};
    for(var key in source) {
        if (Object.prototype.hasOwnProperty.call(source, key)) {
            if (isObject(source[key])) {
                target[key] = cloneDeep(source[key]); // Notice here
            } else{ target[key] = source[key]; }}}return target;
}


Copy the code

What this simple deep copy does not take into account is that when you encounter a circular reference, you get caught up in a circular recursive process that causes the stack to burst

// RangeError: Maximum call stack size exceeded
Copy the code

Do you have any good ideas? You can write codes and discuss them in the comments section.

Third-party deep copy libraries

CloneDeep is also available for Deep Copy (loDash is a good third party open source library, there are many good functions, you can also see the implementation of the source code).

var _ = require('lodash');
var obj1 = {
    a: 1.b: { f: { g: 1}},c: [1.2.3]};var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);
// false

Copy the code

Summary of Copy content

Sum it up with a picture

Today share so much, if you are interested in sharing the content, you can pay attention to the public account “programmer growth refers to north”, or join the technical exchange group, we discuss together.

Advanced technology route

Join us and learn!