Exploring the deep and shallow copies of objects in JS

Source: Internet
Author: User
Tags shallow copy

The difference between a deep copy and a shallow copy

Before you tell the difference between a deep copy and a shallow copy, think about how we usually do it when we copy an object. Isn't it like this?

var testObj1 = {a: 1, b:2}, testObj2=testObj1;testObj1.a = 7;console.log(testObj1);  //{a: 7, b:2}console.log(testObj2);  //{a: 7, b:2}

Did you find the problem? When testObj1 changes, the corresponding properties of testObj2 change. This is a shallow copy, and the so-called deep copy is to do when the testObj1 changes TestObj2 will not follow the change, this is the difference between a shallow copy and a deep copy. At least until I know the difference between the base type and the reference type, I don't know why this is so, what is the basic type and the reference type?

Basic types and reference types

Let's start with a simple example to see what the difference is with the above example:

var num1 = 1, num2 = num1;num1 = 7;console.log(num1);  // 7console.log(num2);  // 1

Obviously, the variable num2 here doesn't change because of the num1 change. In fact, the NUM1 and num2 here are a basic type, and the above two object variables are reference types.

The ECMAScript variable may contain values for two different data types: the base type value and the reference type value. Primitive type values refer to simple data segments that are stored in the stack's memory, meaning that this value is completely stored in a single location in memory. Reference-type values are those that hold objects in the heap memory, meaning that the variables are actually just a pointer to another location in memory where the object is saved.

So, according to my understanding, the basic type is directly existing in the memory of a specific point, is independent of each other, and the reference type stores only a pointer to a specific memory address, when two objects are equal assignment, they are actually pointing to the same memory address, so, when a change, It's not surprising that the other is changing. To make an inappropriate analogy, the basic types are like independent small supermarkets, the door between each other is not the same, and the chain supermarket words are the same, and will change together.

How to implement a deep copy

Now that you know the difference between a deep copy and a shallow copy and why this difference occurs, how do you make a deep copy? I have encountered this problem in business development before, but all using a new empty object, and then traverse the target object's properties, the target object's property values are assigned to the new empty object, resulting in a new object, this object is completely equal to the previous object, but not affected by its changes, So the preliminary realization of the deep copy, the general realization of the following:

var testObj3 = {a: 1, b:2, c:3, d:4};var testObj4 = {};for(var key in testObj3){    testObj4[key] = testObj3[key];}testObj3.a = 7;console.log(testObj3);  //{a: 7, b:2, c:3, d:4}console.log(testObj4);  //{a: 1, b:2, c:3, d:4}

I see this method at that time, although satisfied with my business needs at that time, but there is nothing to improve the place? Is there any other way to implement it?

Is there any other way to implement it?

I did not think about this problem before, I know that occasionally see this article, this article provides the other way is I did not know, is to fill the blind area of my knowledge before, here is thanks. In summary, there are several ways to do this:

  • 1. by Onject.assign ()
    • Object.assign is a method for merging objects introduced in ES6, which can be used to see the document
    • This method I used to be not much, but some of the recent projects have been used to know this property, in fact, it should be possible to think that can be used for deep copy, the general usage is as follows:
    var obj1 = {x: 1, y:2}, obj2 = Object.assign({}, obj1);obj2.x = 7;console.log(obj1);  //{a: 7, b:2}console.log(obj2);  //{a: 1, b:2}
    • As you can see, the object is perfectly deep copied, and the new object is not affected by the original object, but when the object is nested object?
    var obj3 = {x:1, y:2, z:{name: 3}}, obj4 = Object.assign({}, obj3);obj4.z.name = 7;console.log(obj3);  //{x:1, y:2, z:{name: 7}}console.log(obj4);  //{x:1, y:2, z:{name: 7}}
    • It turns out that for nested objects, the object.assign () deep copy is invalidated, so thatobject.assign () can only implement deep copies of one-dimensional objects
  • 2. Via Json.parse (json.stringify (obj))
      • This method is also in the previous period of time friends asked me deep copy of the method when occasionally found, before it is useless, it is quite useful:
    var obj5 = {x: 1, y:2}, obj6 = JSON.parse(JSON.stringify(obj5));obj6.x = 7;console.log(obj5);  //{x: 1, y:2}console.log(obj6);  //{x: 7, y:2}
      • But does this approach solve the deep-copy problem of multidimensional objects?
    var obj9 = {x:1, y:2, z:{name: 3}}, obj10 = Object.assign({}, obj3);obj10.z.name = 7;console.log(obj9);  //{x:1, y:2, z:{name: 3}}console.log(obj10);  //{x:1, y:2, z:{name: 7}}
      • From the code point of view, this method perfectly implements the deep copy of the multidimensional object, but this small change will find the problem:
    var obj9 = {x:1, y:2, z:{name: 3, func: function(){}}}, obj10 = Object.assign({}, obj3);obj10.z.name = 7;console.log(obj9);  //{x:1, y:2, z:{name: 3, func: function(){}}}}console.log(obj10);  //{x:1, y:2, z:{name: 7}}
      • Yes, when there is a function in the multidimensional object, and there is no copy, this is why the MDN on this method has an explanation:

        Undefined, arbitrary functions, and symbol values are ignored during serialization (when they occur in property values of non-array objects) or converted to null (when appearing in an array).

      • Therefore, Json.parse (json.stringify (obj)) can copy multidimensional objects, but cannot deep copy objects containing undefined, function, symbol values.

Areas that can be improved
    • The first way to traverse all the properties through For.in is to iterate over the properties on the prototype chain, which is generally not the case, so it's best to use Obj.hasownproperty (key) for filtering, and of course, depending on your business needs.
    • A few of the latter methods are not perfect for deep copies of multidimensional arrays, so they have to be sacrificed: recursion. To put it simply, create a new object, then iterate through the list of all the properties of the original object through Object.keys (), and then iterate through the lists, and if there is a subset, the object is recursively recursive and eventually gets the deep copy of the object:
function deepCopy(obj) {    let result = {};    let keys = Object.keys(obj), key=null, tem=null;    for(var i=0; i<keys.length; i++) {        key = keys[i];        temp = obj[key];        if (temp && typeof temp === ‘object‘) {            result[key] = deepCopy(temp);        }        else{            result[key] = temp;        }    }    return result}console.log(deepCopy({x:1, y:3}))var obj7 = {x:1, y:3, z:{name: 7, func: function(){}}};var obj8 = deepCopy(obj7);obj8.z.name = 8;console.log(obj8);  // {x:1, y:3, z:{name: 8, func: function(){}}} console.log(obj7)   // {x:1, y:3, z:{name: 7, func: function(){}}}

In this way, you get a compatible multidimensional object, and you can implement a deep copy of a special value such as a Function,null,symbol value. We can also use third-party libraries for deep copies, such as JQuery's $.extend and Lodash _.clonedeep.

Treatment for a particular case

Originally the above code has been almost perfect to achieve deep copy, but, there is a special case, that is, when the copy of the object is a circular reference to the object, then to recursion, then the endless will explode stack. Like this:

var obj1 = {    x: 1,     y: 2};obj1.z = obj1;var obj2 = deepCopy(obj1);

I refer to the solution is to add a layer of stock rate judgment, although I think there is no real meaning of this situation, but as a threshold to deal with it:

function deepCopy(obj, parent) {    let result = {};    let keys = Object.keys(obj), key=null, tem=null, parent_=parent;    if (parent_) {        if (parent_.originalParent === obj) {            return parent_.curentParent;        }        parent_ = parent_.parent;    }    for(var i=0; i<keys.length; i++) {        key = keys[i];        temp = obj[key];        if (temp && typeof temp === ‘object‘) {            result[key] = deepCopy(temp, {                originalParent: obj,                curentParent: result,                parent: parent            });        }        else{            result[key] = temp;        }    }    return result}obj1.z = obj1;var obj2 = deepCopy(obj1);console.log(obj2); //很多东西

In this way, a deep copy function is done, the same applies to arrays, after all, the array is also a special object, of course, the use of third-party libraries in real projects may be more convenient, many times the wheel is not to use in the project but to understand the structure of the wheel, this is very important, Then will continue to build wheels, this is a deep in the process of the knee, refueling!

Reference article:

    • Low threshold for thorough understanding of deep and shallow copies in JavaScript

Exploring the deep and shallow copies of objects in JS

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.