Get straight to the point
JavaScript data types
Five simple data types (also known as basic data types) :Undefined, Null, Boolean, Number, and String;
1 complex data type: Object;
Basic data types (five simple data types) : Data stored directly on the stack
Reference types (complex data types Object) : Store references to the Object in the stack. The real data is stored in the heap memory
Shallow clone
Underlying data types
I personally feel that… There is no deep clone or shallow clone for basic data types.
Because of the nature of data types, the base data type and reference type are not the same when assigning ⬇️
// Basic data type
var a = 'I'm the value of variable A';
var b = a;
console.log(a); // I am the value of variable A
console.log(b); // I am the value of variable A
b = 'I'm the value of variable B';
console.log(a); // I am the value of variable A
console.log(b); // I am the value of variable b
Copy the code
Code above we declare a variable a is for me the value of the variable a, b then declare variables, and the variable assigned to the variable b, the output, it is concluded that variable output values of a and b are the same Then we changed the value of the variable b alone output again found variable output values of a and b, can prove that they are two values exist alone, They’re not related to each other, so even if var b = a, they’re just adding a variable b and a value.
Complex data types (reference types)
// Reference type
var obj1 = {
a: 'a'.b: 'b'
}
var obj2 = obj1;
console.log(obj1); // { a: 'a', b: 'b' }
console.log(obj2); // { a: 'a', b: 'b' }
obj2.b = 'bb';
console.log(obj1); // { a: 'a', b: 'bb' }
console.log(obj2); // { a: 'a', b: 'bb' }
Copy the code
As you can see in the above code, in the direct assignment of the reference type, declare the variable obj1 as an object, and then assign the variable obj1 to obj2. The output variable obj1 and variable obj2 are the same. The value of obj1.b has also changed. That’s what we’re saying. The assignment of a reference type is just a pointer to a reference to an object in memory, so obj1 and obj2 refer to the same object in memory, so when one changes, the other changes.
Insert deep clone image picture, I draw, about the meaning of understanding
A deep clone
We want to achieve a deep clone, is a complete clone out of a brand new object in memory
Imperfect proclone —Object.assign()
The object.assign () method is used to copy the values of all enumerable properties from one or more source objects to target objects. It will return the target object. ———————— MDN Web Docs
Object.assign(target, ... sources)
The object.assign () method takes two arguments, the first is the target function and the second is the source Object. The method copies the enumerable properties of the source Object to the target function and returns the target Object. For example, let’s see how this method works with ⬇️
// Declare target objects and source objects
const target = { a: 1.b: 2 };
const source = { b: 4.c: 5 };
// Copy enumerable properties from the source object to the target object, with the same key value overwrite
const returnedTarget = Object.assign(target, source);
console.log(source); // { b: 4, c: 5 }
console.log(target); // { a: 1, b: 4, c: 5 }
console.log(returnedTarget); // { a: 1, b: 4, c: 5 }
Copy the code
In the current situation, it is true that the source object properties are copied to the target object, seems to be able to achieve deep cloning, then let’s look at the following example ⬇️
// Declare target objects and source objects
const target = {};
const source = {
a: 1.b: {
ba: 'ba'.bb: 'bb'
},
c: function () {
console.log('c')}};// Copy enumerable properties from the source object to the target object, with the same key value overwrite
const returnedTarget = Object.assign(target, source);
console.log(target); // { a: 1, b: { ba: 'ba', bb: 'bb' }, c: [Function: c] }
console.log(source); // { a: 1, b: { ba: 'ba', bb: 'bb' }, c: [Function: c] }
console.log(returnedTarget); // { a: 1, b: { ba: 'ba', bb: 'bb' }, c: [Function: c] }
target.b.ba = 'ba2';
console.log(target); // { a: 1, b: { ba: 'ba2', bb: 'bb' }, c: [Function: c] }
console.log(source); // { a: 1, b: { ba: 'ba2', bb: 'bb' }, c: [Function: c] }
console.log(returnedTarget); // { a: 1, b: { ba: 'ba2', bb: 'bb' }, c: [Function: c] }
Copy the code
The object.assign () method works fine when we’re not dealing with the second or deeper layer of complex data types. However, if the Object contains another layer of Object or Array reference types, they will still be stored Pointers. Instead of actually copying a new Object or Array, object.assign () isn’t perfect, so let’s try ———— serialization instead
Imperfect proclone —JSON.stringify()
andJSON.parse()
Serialization means converting an object or some other type of data structure into a storable format (for example, a file or buffer). In JavaScript, you can serialize a value into a jSON-formatted string by calling json.stringify (). CSS values by calling the CSSStyleDeclaration. GetPropertyValue () function to serialize. ———————————— MDN Web Docs
Now let’s talk about the simplest pseudo-deep clone (not the official term) that does most of the work, but still falls short: serializing and deserializing objects using json.stringify () and json.parse () methods. Again, the above example ⬇️
// Declare the source object
let source = {
a: 1.b: {
ba: 'ba'.bb: 'bb'
},
c: function () {
console.log('c')}};// Assign the target object by serializing and then deserializing the source object
let target = JSON.parse(JSON.stringify(source))
console.log(source); // { a: 1, b: { ba: 'ba', bb: 'bb' }, c: [Function: c] }
console.log(target); // { a: 1, b: { ba: 'ba', bb: 'bb' } }
target.b.ba = 'ba2';
console.log(source); // { a: 1, b: { ba: 'ba', bb: 'bb' }, c: [Function: c] }
console.log(target); // { a: 1, b: { ba: 'ba2', bb: 'bb' } }
Copy the code
The serialization and deserialization methods above clone the source object to the target object, not only the first layer of the same attributes, the second layer of the object does not point to the same object, this seemingly perfect method has several shortcomings
- He can’t clone special objects like functions, regexps, etc
- Object constructor is discarded and all constructors point to Object
- Object has a circular reference and an error is reported
The following example is the interviewer: please implement a deep clone “> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >
// constructor
function person(pname) {
this.name = pname;
}
const Messi = new person('Messi');
/ / function
function say() {
console.log('hi');
};
const oldObj = {
a: say,
b: new Array(1),
c: new RegExp('ab+c'.'i'),
d: Messi
};
const newObj = JSON.parse(JSON.stringify(oldObj));
// The function cannot be copied
console.log(newObj.a, oldObj.a); // undefined [Function: say]
// Sparse array copy error
console.log(newObj.b[0], oldObj.b[0]); // null undefined
// Cannot copy the re object
console.log(newObj.c, oldObj.c); // {} /ab+c/i
// The constructor points to an error
console.log(newObj.d.constructor, oldObj.d.constructor); // [Function: Object] [Function: person]
Copy the code
We can see that cloning of functions, regular objects, sparse arrays, and so on can cause accidents and constructor pointing errors.
const oldObj = {};
oldObj.a = oldObj;
const newObj = JSON.parse(JSON.stringify(oldObj));
console.log(newObj.a, oldObj.a); // TypeError: Converting circular structure to JSON
Copy the code
Object of the circular reference will throw an error. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >
Implementation of deep cloning (code problems, only for reference)
It seems that we want to use the existing method is not able to achieve deep cloning, so we need to write their own methods to achieve deep cloning, we have to remember the idea is two points
- Determine the data type and process it separately
- recursive
/** * constant * @type {string} */
const TYPE_OBJECT = '[object Object]';
const TYPE_ARRAY = '[object Array]';
@param obj source object * @returns {string} Object type */
function typeToString(obj) {
return Object.prototype.toString.call(obj)
}
/** * clone Object * @param oldObj source Object * @returns {Object} Returns the cloned Object */
function deepClone(oldObj) {
let newObj;
if ( oldObj === null ) {
return null
}
if ( typeofoldObj ! = ='object') {
return oldObj
}
switch (typeToString(oldObj)) {
case TYPE_OBJECT:
newObj = {}
break;
case TYPE_ARRAY:
newObj = [];
break;
}
for (let i in oldObj) {
newObj[i] = deepClone(oldObj[i]);
}
return newObj
}
Copy the code
The above is my own hand-written implementation of the deep clone do not copy, write only a small demo, can not be used in the production environment, the judgment of the data type is limited, and does not deal with object inheritance constructor pointing problem, also does not solve the problem of circular reference look at the general idea
Best practices ————Lodash_.cloneDeep
What is Lodash? Lodash is a consistent, modular, and high-performance JavaScript utility library that can be found in another blog post: Getting Started with Lodash: Getting Started with Lodash and 😝😝😝😝 college
use
import _ from 'lodash';
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
Copy the code
Use is so used, look at the source code is how to achieve, can say that LoDash in the deep clone method is really complete and complete, the judgment of the project test 20 items, not only have the judgment of data type, and the judgment of floating point number, how many bits of floating point number, anyway is a lot of judgment, as well as the boundary consideration.
OH MY GOD, use it!
PS: This article has no animation elements 😢😢😢
I’m a front end warrior, a front end elementary school.