Shallow copy and deep copy

preface

In JavaScript, we divide data into primitive data types (raw values) and reference types

  • Values of primitive data types are accessed by value, and values of primitive types are immutable
  • The values of reference types are accessed by reference, and the values of reference types are dynamically variable
var zxx = 100;
var zxx1 = 100;
console.log(zxx === zxx1) // true
var zxx2 = {a: 1, b: 2};
var zxx3 = {a: 1, b: 2};
console.log(zxx2 === zxx3) // falseTwo different objectsCopy the code

  • Comparisons of basic data types are worth comparing
  • A comparison of reference types is a comparison of reference addresses

In view of the characteristics of the above data types, we can initially think: the so-called shallow copy and deep copy may be copies of values and references (basic data types are copies of values, no distinction is made). In general, the copy objects we are dealing with are also for reference types.

  • Shallow copy is a copy of a layer, deep object level copy reference; Deep copy is the copying of multiple layers, each level of data is copied;
  • Both the shallow copy and the deep copy are for reference data types only. The shallow copy copies Pointers to an object, not the object itself. The old and new objects still share the same memory. However, a deep copy will create another identical object. The new object does not share memory with the original object, and modification of the new object will not change the original object.
  • Difference: The shallow copy copies only the attributes of the first layer of the object, while the deep copy copies the attributes of the object recursively.

Problems with not using copies

var zxxArr = ["One"."Two"."Three"]
var zxxArrs = zxxArr
zxxArrs[1] = "love"; Console. log(zxxArr) // ["One"."love"."Three"]
console.log(zxxArrs) // ["One"."love"."Three"]Copy the code

Shallow copy:

Copy an object at a shallow level, copying only one layer of the object’s properties, not including the reference type data in the object.

Shallow copy of array:

Solution 1: Array slice method

var zxxArr = ["One"."Two"."Three"]
var zxxArrs = zxxArr.slice(0)
zxxArrs[1] = "love";
console.log(zxxArr) // ["One"."Two"."Three"]
console.log(zxxArrs) // ["One"."love"."Three"]Copy the code

Solution 2: Array concat method

var zxxArr = ["One"."Two"."Three"]
var zxxArrs = zxxArr.concat()
zxxArrs[1] = "love";
console.log(zxxArr) // ["One"."Two"."Three"]
console.log(zxxArrs) // ["One"."love"."Three"]Copy the code

Solution 3: ES6 extension operators…

var zxxArr = ["One"."Two"."Three"]
var zxxArrs = [...zxxArr]
zxxArrs[1] = "love";
console.log(zxxArr) // ["One"."Two"."Three"]
console.log(zxxArrs) // ["One"."love"."Three"]Copy the code

Shallow copy of object:

The first way

// Copy only the shallow copy of the first layerfunction simpleCopy (obj1) {
    var obj2 = Array.isArray(obj1) ? [] : {}
    for (let i in obj1) {
        obj2[i] = obj1[i]
    }
    return obj2
}
var zxxObj = {
    age: 18,
    nature: ['smart'.'good'],
    names: {
        name1: 'zxx',
        name2: 'xka'
    },
    love: function () {
        console.log('zxx is a great girl')
    }
}
var newZxxObj = simpleCopy(zxxObj)
newZxxObj.age = 8
newZxxObj.nature.push('why')
newZxxObj.names.name1 = 'why zxx'
newZxxObj.love = function () {
    console.log('zxx is 18 years old'} console.log(zxxobj.age) // 18 Basic data types do not change console.log(zxxobj.nature) // ["smart"."good"."why"] reference types change console.log(zxxObj['names']) // {name1: "why zxx", name2: "xka"} The reference type will change console.log(zxxObj['love']) // ƒ () {console.log('zxx is a great girl')}Copy the code

Second method: object. assign (can only handle objects with one layer of depth)

var zxxObj = {
    age: 18,
    nature: ['smart'.'good'],
    names: {
        name1: 'zxx',
        name2: 'xka'
    },
    love: function () {
        console.log('zxx is a great girl')
    }
}
var newZxxObj = Object.assign({}, zxxObj);
newZxxObj.age = 8
newZxxObj.nature.push('why')
newZxxObj.names.name1 = 'why zxx'
newZxxObj.love = function () {
    console.log('zxx is 18 years old')
}
console.log(zxxObj.age) // 18
console.log(zxxObj.nature) //  ["smart"."good"."why"]
console.log(zxxObj['names']) // {name1: "why zxx", name2: "xka"}
console.log(zxxObj['love']) // ƒ () {console.log('zxx is a great girl')}Copy the code

Var newZxxObj = {… zxxObj}

var zxxObj = {
    age: 18,
    nature: ['smart'.'good'],
    names: {
        name1: 'zxx',
        name2: 'xka'
    },
    love: function () {
        console.log('zxx is a great girl') } } var newZxxObj = {... zxxObj} newZxxObj.age = 8 newZxxObj.nature.push('why')
newZxxObj.names.name1 = 'why zxx'
newZxxObj.love = function () {
    console.log('zxx is 18 years old')
}
console.log(zxxObj.age) // 18
console.log(zxxObj.nature) //  ["smart"."good"."why"]
console.log(zxxObj['names']) // {name1: "why zxx", name2: "xka"}
console.log(zxxObj['love']) // ƒ () {console.log('zxx is a great girl')}Copy the code

Complete sample

var person = {
    name: 'tt',
    age: 18,
    friends: ['oo'.'cc'.'yy']}function shallowCopy(source) {
    if (!source || typeof source! = ='object') {
        throw new Error('error');
    }
    var targetObj = source.constructor === Array ? [] : {};
    for (var keys in source) {
        if (source.hasOwnProperty(keys)) {
            targetObj[keys] = source[keys]; }}return targetObj;
}

var p1 = shallowCopy(person);

console.log(p1)Copy the code

In the code above, we created one
shallowCopyFunction, which takes an argument that is the object to be copied.

  • First you create an object
  • thenfor... inTo avoid looping over properties that would be iterated over the prototype, use thehasOwnPropertyThe restriction loop adds each property and value of the copied object to the created object only on the object itself
  • Finally, this object is returned

By testing, we get p1, which is almost identical to the Person object. What’s the difference between this result and an assignment like person?

var p2 = person; Person.name = person.name ='tadpole';
person.age = 19; 
person.friends.push('tt')

p2.name // tadpole
p2.age // 19
p2.friends // ["oo"."cc"."yy"."tt"]

p1.name // tt
p1.age // 18
p1.friends // ["oo"."cc"."yy"."tt"]Copy the code

Above we created a new variable
p2That will be
personAssigned to
p2And then compare the two variables

Deep copy:

Shallow copy Because it copies only the properties of one layer of objects, when there are child objects, the child objects will affect each other. So, a deep copy is a copy of an object and all its children

Method one:

Json.stringify converts the object into a string, and json.parse converts the string into a new object.



undefined

2, will ignore symbol

Cannot serialize function, cannot copy function

Const a = {val:2}; a.target = a; Copying A will result in stack overflow because of infinite recursion

5. Failed to handle RegExp, Date, Set, Map, etc

Cannot handle regex

7. Discard object constructor. After deep copy, whatever the original constructor of this Object was, after deep copy, it will become Object.

var zxxObj = {
    age: 18,
	why: undefined,
	why1: Symbol('why1'),
    nature: ['smart'.'good'],
    names: {
        name1: 'zxx',
        name2: 'xka'
    },
    love: function () {
        console.log('zxx is a great girl')
    }
}
var newZxxObj = JSON.parse(JSON.stringify(zxxObj))
newZxxObj.age = 8
newZxxObj.nature.push('why')
newZxxObj.names.name1 = 'why zxx'

newZxxObj.love = function () {
    console.log('zxx is 18 years old')
}
console.log(zxxObj.age) // 18
console.log(zxxObj.nature) // ["smart"."good"]
console.log(zxxObj['names']) // {name1: "zxx", name2: "xka"}
console.log(zxxObj['love']) // ƒ () {console.log('zxx is a great girl')}
console.log(newZxxObj['love']) // undefined functionThere's no way to convert it to JSON. console.log(newZxxObj) // {age: 8, nature: Array(3), names: Object, love:function} why why1 love will be ignoredCopy the code

In circular reference cases, an error is reported.

let obj = {
    a: 1,
    b: {
        c: 2,
        d: 3
    }
}
obj.a = obj.b;
obj.b.c = obj.a;

let b = JSON.parse(JSON.stringify(obj));
// Uncaught TypeError: Converting circular structure to JSONCopy the code

Method two: cyclic recursion

function deepClone(initalObj, finalObj) {
    var obj = finalObj || {};
    for (var i ininitalObj) { var prop = initalObj[i]; // Avoid endless loops caused by cross-referencing objectsif(prop === obj) {
            continue;
        }
        if (typeof prop === 'object') {
            obj[i] = (prop.constructor === Array) ? [] : {};
            arguments.callee(prop, obj[i]);
        } else{ obj[i] = prop; }}return obj;
}
var zxxObj = {
    age: 18,
    nature: ['smart'.'good'],
    names: {
        name1: 'zxx',
        name2: 'xka'
    },
    love: function () {
        console.log('zxx is a great girl')
    }
}
var newZxxObj = deepClone(zxxObj);
newZxxObj.age = 8
newZxxObj.names.name1 = 'newzxx'
console.log(zxxObj)
console.log(newZxxObj)Copy the code

The output

The $.extend method in jquery and Zepto can be used as a deep copy

var $ = require('jquery');
var newObj = $.extend(true, {}, obj);Copy the code

Example:

function deepCopy(source) {if(!source || typeof source! = ='object'){
     throw new Error('error');
   }
   var targetObj = source.constructor === Array ? [] : {};
   for(var keys in source) {if(source.hasOwnProperty(keys)){
         if(source[keys] && typeof source[keys] === 'object'){
           targetObj[keys] = source[keys].constructor === Array ? [] : {};
           targetObj[keys] = deepCopy(source[keys]);
         }else{
           targetObj[keys] = source[keys]; }}}return targetObj;
}
var obj1 = {
    arr: [1, 2, 3],
    key: {
        id: 22
    },
    func: function() {
        console.log(123)
    }
}

var obj2 = deepCopy(obj1);

obj1.arr.push(4);

obj1.arr // [1, 2, 3, 4]
obj2.arr // [1, 2, 3]
obj1.key === obj2.key // false
obj1.func === obj2.func // trueCopy the code

For deep-copied objects, changing the source object has no effect on the resulting object. It’s just that the methods of the source object are lost in the copying process because of serialization
JavaScriptObject, all functions and prototype members are intentionally ignored.