I have been busy with the interview for some time. Now I have settled down. I would like to sort out my experiences during this period. This article is about deep copy this topic, this is a question of high frequency in the interview, review their problem solving process, found that the understanding of the problem is not enough comprehensive thorough, so I have collected some selected blog posts, made a summary study notes, as I am the first blog post on the path of technical writing.

What is deep copy?

The best way to see a problem is to look at the essence through the phenomenon, find the definition of the essence of things, understand its origin, transformation and integration, and then understand it thoroughly. So let’s talk a little bit about what deep copy is, just to get to the bottom of what it is.

Copy in fact, the popular word is to make a copy, or can simply be understood as a clone of the same. In the front-end domain, deep copy of JS has its special characteristics compared to some common programming languages. To clarify this, we have to mention its closely related cousin – “shallow copy”.

Shallow copy

Shallow copy definitions:

When we take an object as ontology and copy an object with related attributes and worth copies, the attribute values of some original types, such as Number and String, are stored somewhere in the computer memory. When copying objects, a simple assignment is made to the related attribute values of the new object. You can copy the contents of a variable into another variable’s memory.

But for reference types such as Object or Array, the attribute value of the Object actually describes a memory address. If the source attribute value is a reference to one object, it simply copies its reference value, and when it changes it affects another object (the original object related attribute).

Deep copy

To paraphrase a selection of nuggets posts, we get the following definition:

To make a complete copy of an object out of memory, a new area of heap memory is created to hold the new object, and modification of the new object does not affect the original object.

How to implement shallow copy?

Before we talk about deep copy implementations, let’s document some common shallow copy implementations.

1. Object.assign()

Definition: The object.assign () method is used to assign the values of all enumerable properties from one or more source objects to target objects. It will return the target object.

const target = { a: 1.b: 2 };
const source = { b: 4.c: 5 };

const returnedTarget = Object.assign(target, source);

console.log(target);
// expected output: Object { a: 1, b: 4, c: 5 }

console.log(returnedTarget);
// expected output: Object { a: 1, b: 4, c: 5 }

Copy the code

Use object.assign () to copy objects. Note:

  • Deep copy is not possible; if the source value is a reference to an object, it simply copies its reference value.
  • Inherited properties and non-enumerable properties cannot be copied
  • Primitive types are wrapped as objects
  • An exception interrupts subsequent copy tasks

2. The _. Clone method of lodash

Create a shallow copy of value.

Note: This method references self-structured Clone algorithm and supports arrays, array Buffers, Booleans, date Objects, maps, numbers. Object objects, Regexes, sets, strings, symbols, and typed arrays. The enumerable properties of the Arguments object are copied as normal objects. Non-copiable objects such as Error Objects, functions, DOM Nodes, and WeakMaps return empty objects.

Eg :

var objects = [{ 'a': 1 }, { 'b': 2 }];
 
var shallow = _.clone(objects);
console.log(shallow[0] === objects[0]);
// => true

Copy the code

3…. Extended operator

Object extension operator (…) Retrieves all traversable properties of the parameter object and copies them to the current object.

let z = { a: 3.b: 4 };
letn = { ... z }; n// { a: 3, b: 4 }
Copy the code

4. Array.prototype.concat()

The concat() method is used to merge two or more arrays. This method does not change an existing array, but returns a new array. Arrays and/or values will be merged into a new array. If all valueN arguments are omitted, concat returns a shallow copy of the existing array that called the method.

const array1 = ['a'.'b'.'c'];
const array2 = ['d'.'e'.'f'];
const array3 = array1.concat(array2);

console.log(array3);
// expected output: Array ["a", "b", "c", "d", "e", "f"]
Copy the code

5.Array.prototype.slice()

The slice() method returns a new array object that is a shallow copy (including begin, but not end) of the array determined by begin and end. The original array will not be changed.

const animals = ['ant'.'bison'.'camel'.'duck'.'elephant'];

console.log(animals.slice(2));
// expected output: Array ["camel", "duck", "elephant"]

console.log(animals.slice(2.4));
// expected output: Array ["camel", "duck"]

console.log(animals.slice(1.5));
// expected output: Array ["bison", "camel", "duck", "elephant"]
Copy the code

Say that finish above, the following to our highlight, deep copy implementation.

Base edition deep copy

First, we’ll look at json.stringfy and json.parse, which are often used for deep copy.

let obj = {
  a: 1.b: {c: 2}}let objClone = JSON.parse(JSON.stringfy(obj))
Copy the code

This approach is very handy for implementing deep copies in simple scenarios.

However, in some business scenarios where replication or being used frequently, we should avoid it because it has disadvantages such as not being able to handle functions and regular objects. When both are processed based on json.stringify and json.parse, the resulting re is no longer a re (becomes an empty object) and the resulting function is no longer a function (becomes null).

Here’s the kicker 👇🏻 :

Speaking from personal experience, if you want to achieve an average level of proficiency in the deep copy question during the interview process, you should be able to write at least the following deep copy code.

function deepClone (Obj) {
  if (typeof obj === 'Object') {
    let newObj = Obj instansof Array ? [] : {}
    for (let key  in obj) {
      newObj[key] = deepClone(obj[key])
    }
    return newObj
  } else {
    return obj
  }
}
Copy the code

The _. CloneDeep method of the lodash library

This method is similar to _.clone except that it copies the value recursively. (Also called deep copy).

var objects = [{ 'a': 1 }, { 'b': 2 }];
 
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
Copy the code

Advanced deep copy

But as anyone beyond experience can tell, the base deep copy isn’t perfect, for example:

  • A circular reference
  • Performance optimization
  • The other types

A circular reference

If the object we are copying has a circular reference structure (that is, if the object’s attributes refer to itself indirectly or directly), then the deep-copy function above will loop it into an infinite loop.

Here’s the answer to that question from a selection of blog posts:

Solve the problem of circular reference, we can create a extra storage space, to store the current objects and copy the object correspondence, when need to copy the current object, go to the storage space, find ever copy this object, if any direct return, if not to copy, so clever resolve problem of circular references.

This storage space needs to be able to store key-value data, and key can be a reference type, we can choose Map data structure:

  • checkmapIs there any cloned object in
  • Yes. – Straight back
  • None – takes the current object askey, clone the object asvalueFor storage
  • Continue to clone
function clone(target, map = new Map(a)) {
    if (typeof target === 'object') {
        let cloneTarget = Array.isArray(target) ? [] : {};
        if (map.get(target)) {
            return map.get(target);
        }
        map.set(target, cloneTarget);
        for (const key in target) {
            cloneTarget[key] = clone(target[key], map);
        }
        return cloneTarget;
    } else {
        returntarget; }};Copy the code

If WeakMap is promoted to Map to make the code achieve the finishing touch. Check out the original blog post for details

Here is just a simple note on weakMap and weak references:

A WeakMap object is a set of key/value pairs where the keys are weakly referenced. The key must be an object, and the value can be arbitrary.

What is a weak reference?

In computer programming, a weak reference, as opposed to a strong reference, is a reference that cannot be guaranteed that the object it refers to will not be collected by the garbage collector. An object that is referred to only by weak references is considered inaccessible (or weakly accessible) and can therefore be reclaimed at any time.

Advanced optimization

We used for in to traverse both groups and objects. In fact, for in is very inefficient in traverse. See the original post for details

function clone(target, map = new WeakMap(a)) {
    if (typeof target === 'object') {
        const isArray = Array.isArray(target);
        let cloneTarget = isArray ? [] : {};

        if (map.get(target)) {
            return map.get(target);
        }
        map.set(target, cloneTarget);

        const keys = isArray ? undefined : Object.keys(target);
        forEach(keys || target, (value, key) = > {
            if (keys) {
                key = value;
            }
            cloneTarget[key] = clone2(target[key], map);
        });

        return cloneTarget;
    } else {
        returntarget; }}Copy the code

The other types

In the above code, we only consider the ordinary object and array data types. In fact, all reference types are far more than these two, such as function and NULL, Symbol, Map, Set…

See the original blog post and code for details

summary

The answer to a question usually reflects which aspects of our knowledge we have mastered well and which aspects we lack or have not yet understood.

We should learn to understand a problem from the breadth, learn to solve a problem, divergent thinking to see the problem; Also want to a problem of a solution to think through, go deep into its essential source to longitudinal analysis, learn to trace back to the source.

Thus, a small deep copy hides a lot of knowledge:

Basic implementation

  • Recursive ability

A circular reference

  • Consider all sides of the problem
  • Understand the true meaning of WeakMap

A variety of types

  • Consider the rigor of the problem
  • Create a variety of reference types of methods, JS API proficiency
  • Accurate judgment of data types and understanding of data types

Universal traversal:

  • Write code with performance optimization in mind
  • Understand the efficiency of centralized traversal
  • Code abstraction capability

Copy function:

  • The difference between an arrow function and a normal function

  • Regular expression proficiency

The appendix

How to write a deep copy that will impress your interviewer?

Shallow copy and deep copy

Shallow and deep copies of JavaScript

Object.assign

lodash

ECMAScript introduction to 6

Afterword.

I am Tong Love, a front-end programmer indulging in the ocean of lonely code 👨🏻💻

Welcome to 🙏

Look forward to the same efforts to explore the partners in the exchange 🙌