When you read the articles in the team’s official account, you will gain a lot. The hard dishes in the public account also make people eat, this issue to give you the second bomb of the front of the idle court walking chat, “a paper to find out the depth of ES copy”. Here is a refreshment for you to talk about (around the Bear Town building ~~~).

Why light copy

Depth of the copy of the conversation in the circle can not be little, record collation of this information involved in the knowledge point;

  • When an object needs to change from a mutable state to an immutable state, the changed object does not affect the original object.
  • If an object needs to change from an immutable state to a mutable state, the changed object does not affect the original object.

Shallow depth copy origin

Copy means many. When a document or an article has a copy, a copy, or a duplicate, it is no longer one but many.

The data type
  • Basic types: String, Number, Boolean, Null, Undefined, Symbol

  • Reference type: Object

Memory management (heap and stack)

Memory management refers to the allocation and use of computer memory resources by software. At this point, JavaScript engines have to be mentioned, and here is a brief explanation.

A JavaScript engine is a virtual machine that specializes in JavaScript scripting and is typically shipped with a web browser.

JavaScript itself is an interpreted language, weakly typed, and edited only when the program is running.

In terms of the distribution of JavaScript engine usage, it can be seen that the main engine is Chrome V8, and Node.js is also a JavaScript running environment based on Chrome V8 engine.

[Note] : JavaScript engines are not to be confused with the browser’s core rendering engine, which is a core part of the browser’s web processing process and a cooperative relationship. JavaScript has no concept of input or output. It is a scripting language that runs in the Host Environment, and any mechanism for communicating with the outside world is provided by the Host Environment (browser Environment, Node Environment, etc.).

On this basis, let’s look at stacks and heaps in JavaScript engines:

The stack The heap
Storage type Basic data types Reference data type
allocation Statically allocated Dynamic allocation
The size small big
access According to the value of access According to the site visit

JavaScript variables do not have data types, values have data types, and variables can hold any data type at any time;

Conclusion:

  • Shallow copy is a copy of a JS reference data type.
  • The memory space where shallow and deep copying occurs is the heap space.
  • Stack space and heap space in JS are provided by the JS engine.
  • When you access data in heap space, you actually access its pointer. The pointer is unchanged, but the data is mutable, so there is a deep and shallow copy.

How to implement shallow copy

Q: When will light copy be used? A: When the data type is reference data, operations need to be performed on the data without affecting the source data.

Shallow copy: applies to reference types whose values are primitive types. Deep copy: applies to reference types in which values contain reference types;

Take a look at the following common scenario:

// The values of the foo and bar variables are reference types, and the stored values within reference types are primitive types.
var foo = {
	name: 'foo'.age: '20'
};
var bar = [1.'2'.3];
// Try copying in normal mode

var fooCopy = foo;
fooCopy.name = 'fooCopy';
foo.age = '21';

console.log(foo); // {name: "fooCopy", age: "21"} The original name is changed
console.log(fooCopy); // { name: "fooCopy", age: "21" }
Copy the code

The result is not as expected. Try shallow copy.

Shallow copy
// 1. Object copy -- ES6
varfooEs6Copy = {... foo}; fooEs6Copy.name ='fooEs6Copy'; // Perform the operation
console.log(foo); // {name: "foo", age: "20"}
console.log(fooEs6Copy); // {name: "fooEs6Copy", age: "20"}

// 2. Copy objects -- object.assign ()
var fooAssignCopy = Object.assign({}, foo);
fooAssignCopy.name = 'fooAssignCopy'; // Perform the operation
console.log(foo); // {name: "foo", age: "20"}
console.log(fooAssignCopy); // {name: "fooAssignCopy", age: "20"}

// 3. Array copy -- arrayObject.concat(
var barConcatCopy = Array.prototype.concat(bar);
barConcatCopy.push(4); // Perform the operation
console.log(bar);  //  [1, "2", 3]
console.log(barConcatCopy); // [1, "2", 3, 4]

Array copy -- arrayObject.slice(
var barSliceCopy = Array.prototype.slice.apply(bar);
barSliceCopy.push(4); // Perform the operation
console.log(bar);  //  [1, "2", 3]
console.log(barSliceCopy); // [1, "2", 3, 4]
Copy the code

The results are as expected, but let’s change the data structure and try again?

// The values of the foo and bar variables are reference types, and the values stored within reference types are reference types.
var foo = {
	name: 'foo'.age: '20'.other: {
		job: 'Programmer'}};// Try shallow copy
varfooEs6Copy = {... foo}; fooEs6Copy.other.job ='Ape program'; // Perform the operation
console.log(foo); // {name: "foo", age: "20", other: {job: 'app '}}
console.log(fooEs6Copy); // {name: "foo", age: "20", other: {job: 'app '}}
Copy the code

Emm ~, this time also did not meet expectations, then try deep copy?

Deep copy –JSON.parse()

Continue with the foo snippet above;

  • Json.stringify () converts the value to the appropriate JSON format;

  • The json.parse () method parses JSON strings to construct JavaScript values or objects described by the strings.

var fooJsonCopy = JSON.parse(JSON.stringify(foo));
fooJsonCopy.job.push('Ape program');
console.log(foo); // {name: "foo", age: "20", other: {job: 'programmer '}}
console.log(fooJsonCopy); // {name: "foo", age: "20", other: {job: 'app '}}
Copy the code

This satisfies the requirements, but is not the optimal solution, so continue with the following code snippet:

var foo = {
	name: 'foo'.age: Symbol('20'),
  fn: function() {},
	other: undefined
};

var fooJsonCopy = JSON.parse(JSON.stringify(foo));
console.log(foo); // {name: "foo", age: Symbol(20), other: undefined}
console.log(fooJsonCopy);  // {name: "foo"}
Copy the code

Check out the description of the json.stringify () function:

  • undefined, any function andsymbolValue, which is ignored during serialization (when present in the -attribute value of a non-array object) or converted tonull(when appearing in an array). The function,undefinedReturns when converted separatelyundefined, such asJSON.stringify(function(){}) or JSON.stringify(undefined).
  • Executing this method on objects that contain circular references (objects that refer to each other in an infinite loop) throws an error.
  • All tosymbolProperties that are property keys are completely ignored, even if they are mandatory in the replacer parameter.

Q: What is the solution to this problem? A: Encapsulate the copy function.

Deep copy – encapsulating functions
  • SymbolEach from:Symbol()The returned symbol value is unique. A symbol value can be used as an identifier for object properties; This is the only purpose of this data type.
  • WeakMapWeakMapAn object is a collection of key/value pairs whose keys are weakly referenced. The key must be an object, and the value can be arbitrary.

The choice to use WeakMap instead of Map is due to the fact that WeakMap holds a “weak reference” to each key object, which means garbage collection works correctly when no other reference exists.

    function handleDeepCopy(param, storage = new WeakMap(a)) {
      // Not an object or array
      if(! (paraminstanceof Object)) return param;
      // Is an array
      let result = Array.isArray(param) && [] || {};
      // Note: There should be no unrestricted references in actual projects
      // Circular reference - returns stored reference data
      if (storage.has(param)) return storage.get(param);
      // Circular references - Sets a temporary store value to resolve circular references
      storage.set(param, result);
      // Whether to include the Symbol type
      let isSymbol = Object.getOwnPropertySymbols(param);
      // If Symbol type is included
      if (isSymbol.length) {
        isSymbol.forEach(item= > {
          // Check if Symbol stores the type as an object or array
          if (param[item] instanceof Object) {
            result[item] = handleDeepCopy(param[item], storage);
            return;
          }
          result[item] = param[item];
        });
      }
      // The loop here does not contain the Symbol type
      for (let key in param) {
        if (!Object.prototype.hasOwnProperty.call(param, key)) continue;
        result[key] = (param[key] instanceof Object) && 
          handleDeepCopy(param[key], storage) || 
          param[key]
      }
      return result;
    };
Copy the code

At the end

Basically introduced here, I hope this article can bring you help, if you want to know more front-end knowledge, you can pay attention to our big round FE, a technical public number with temperature (warm tips: part of the article at the end of the Easter egg gift oh ~~~)