The preface

This article is mainly to explore how to carry out deepcopy, in fact xiaobian has seen a lot of big guys write deepcopy, but in fact, I also want to write a plug-in with their own after all is the most cool, well, not to say more to begin…

A prelude to

Before we start, let’s introduce a plugin called WHERe-type, which can be used to retrieve data types in various ways. I hope you pay more attention to it

Go straight to code

1. How to determine basic data types

// --------------- defines part ----------------------
const { getTypes } = require('where-type')
// These objects are worthy of deep cloning
const OBJECT_TYPES = [
  {},
  [],
  new Map(),
  new Set(),
  new Error(),
  new Date(),
  $/ / ^
].map((item) = > getTypes(item))
// ------------------- code judgment part ----------------
// All the above types are based on basic data types
const type = getTypes(target)
if(! OBJECT_TYPES.includes(type))return target
Copy the code
  • Using the above variable OBJECT_TYPES, we can determine that if we encounter a type other than these, we consider it a primitive data type
  • However, many people may have some questions, such as function, Symbol type, we consider the basic data type
  • In fact, I personally don’t think functions have to be copied, just use the original function. There are two reasons:
    • One is that functions are usually called directly, either to do logic or to return a value
    • Second, the comparison of functions is really meaningless, at least until now xiaobian has not seen the function used for comparison
    • If some friends are very stubborn, just want to compare then you can use the following notation
if (isFunction(target)) return (... args) => target.call(this, ... args)Copy the code
  • As for how to deal with the Symbol type, we will explain it gradually in the future
  • The next step is to determine which type is filtered above

2. Check the Error and Date types

// ------------ defines the part of the type --------------------
const { getTypes } = require('where-type')
const CONSTRUCT_TYPE = [new Error(), new Date()].map((item) = > getTypes(item))
// --------------- code execution part ------------------
// Handle error and date
if (CONSTRUCT_TYPE.includes(type)) return new target.constructor(target)
Copy the code
  • (Error, Date) (Constructor) (Error, Date) (new) (Error, Date) (Constructor) Since we can get Constructor, we can do a new job ourselves
  • (4). What do you mean by…?
    • The first thing we have to get is an instance, for example of the Date type, for instance Date.
    • Who does date’s prototype chain point to,date.__proto__ === Date.prototypeWe can find the prototype by looking at the equation on the left, which actually has the property Consructor
  • Through the above deduction, we can directlynew target.Constructor(target)Isn’t it just a new instance

3. Judge the type of Symbol

// ----------------- defines part ---------------------
const { getTypes } = require('where-type')
const SYMBOL_TYPE = getTypes(Symbol('1'))
// ----------------- code execution part -----------------
if (SYMBOL_TYPE === type) return Object(Symbol.prototype.valueOf.call(target))
Copy the code
  • We’re throughSymbol.prototype.valueOfTo get the value of Symbol
  • We can use Object to get the new Symbol() value

4. Determine the regular expression

// ----------------- defines part -----------------------
const { getTypes } = require('where-type')
const REGEXP_TYPE = getTypes($/ / ^)

// ---------------- code execution part --------------------
// For regular expressions
if (REGEXP_TYPE === type) {
  const flags = /\w*$/
  const result = new target.constructor(target.source, flags.exec(target))
  result.lastIndex = target.lastIndex
  return result
}
Copy the code
  • In fact, the regular expression judgment is similar to the previous one, but it is worth noting a few details:
    • In fact, the above processing adds an alternate re, for example: the second argument in the regular expression indicates that if the passed argument is invalid, it will be matched from the alternate flags
    • You also need to put lastIndex, which some of you might be wondering, why do you need to do that? Because maybe the re you’re deep copying now has already been matched, and the lastIndex has changed. So we need to put the lastIndex

5. Set and Map

// ----------------- part of the code definition --------------------
const { getTypes } = require('where-type')
const MAP_TYPE = getTypes(new Map())
const SET_TYPE = getTypes(new Set())
// ---------------- part of code execution --------------------
// for set processing
if (SET_TYPE === type) {
  target.forEach((value) = > {
    copyTarget.add(deepCopy(value, weakMap))
  })
  return copyTarget
}

// For map processing
if (MAP_TYPE === type) {
  target.forEach((value, key) = > {
    copyTarget.set(key, deepCopy(value, weakMap))
  })
  return copyTarget
}
Copy the code
  • In fact, the Set and Map processing is much simpler, as can be seen from the above code, nothing more than to create a new constructor to add, etc

At the end

The above is the principle and analysis of deep replication. If you have different opinions, you can raise them directly. Everybody study together. See the source code address below

Source code address

To introduce myself

Actually, I haven’t introduced myself yet. Look at the following:

  • Personal blog
  • Personal Works

Ask questions