JAVASCRIPT

[ Solved – 10 Answers ] JAVASCRIPT – How to correctly clone a JavaScript object?

  • How to correctly clone a JavaScript object?

  • To do this for any object in JavaScript will not be simple or straightforward.
  • we will run into the problem of incorrectly picking up attributes from the object’s prototype that should be left in the prototype and not copied to the new instance.
  • If, for instance, we are adding a clone method to Object.prototype, we will need to explicitly skip that attribute.
  • In addition to non-enumerable attributes, we will encounter a tougher problem when we try to copy objects that have hidden properties.
  • For example, prototype is a hidden property of a function.
  • Also, an object’s prototype is referenced with the attribute __proto__, which is also hidden, and will not be copied by a for/in loop iterating over the source object’s attributes.
  • we think __proto__ might be specific to Firefox’s JavaScript interpreter and it may be something different in other browsers.
  • Not everything is enumerable. we can copy a hidden attribute if we know its name, but we don’t know of any way to discover it automatically.
  • so far another solution is the problem of setting up the prototype inheritance correctly.
  • If your source object’s prototype is Object, then simply creating a new general object with {} will work, but if the source’s prototype is some descendant of Object, then we are going to be missing the additional members from that prototype which we skipped using the hasOwnProperty filter.
  • One solution might be to call the source object’s constructor property to get the initial copy object and then copy over the attributes, but still we will not get non-enumerable attributes.
  • For example, a Date object stores its data as a hidden member:
javascript Code
function clone(obj) 
{
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) 
{
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}

var d1 = new Date();

/* Wait for 5 seconds. */
var start = (new Date()).getTime();
while ((new Date()).getTime() - start < 5000);


var d2 = clone(d1);
alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
  • The date string for d1 will be 5 seconds behind that of d2.
  • A way to make one Date the same as another is by calling the setTime method, but that is specific to the Date class.
  • When we had to implement general deep copying we ended up compromising by assuming that we would only need to copy a plain Object, Array, Date, String, Number, or Boolean .
  • The last 3 types are immutable, so we could perform a shallow copy and not worry about it changing.
  • We further assumed that any elements contained in Object or Array would also be one of the 6 simple types in that list.
READ  How to force browser to reload cached CSS/JS files

code

javascript Code
function clone(obj)
 {
    var copy;

    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) 
{
        copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }
javascript Code
 // Handle Array
    if (obj instanceof Array) 
{
        copy = [];
        for (var i = 0, len = obj.length; i < len; i++)
 {
            copy[i] = clone(obj[i]);
  }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object)
 {
        copy = {};
        for (var attr in obj)
 {
            if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}
  • The above function will work effectively for the 6 simple types, as long as the data in the objects and arrays form a tree structure.
  • That is, there isn’t more than one reference to the same data in the object.

For example:

javascript Code
// This would be cloneable:
var tree = 
{
    "left"  : 
{ "left" : null, "right" : null, "data" : 3 },
    "right" : null,
    "data"  : 8
};

// This would kind-of work, but you would get 2 copies of the 
// inner node instead of 2 references to the same copy
var directedAcylicGraph = 
{
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
javascript Code
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];

// Cloning this would cause a stack overflow due to infinite recursion:
var cylicGraph = 
{
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
cylicGraph["right"] = cylicGraph;

  • Here, we are not using jQuery and only involved in cloning simple objects.
javascript Code
JSON.parse(JSON.stringify(json_original));

  • With jQuery, we can shallow copy with extend:
javascript Code
var copiedObject = jQuery.extend({}, originalObject)

subsequent changes to the copiedObject will not affect the originalObject, and vice versa.

  • To make a deep copy:
javascript Code
var copiedObject = jQuery.extend(true, {}, originalObject)

  • If we do not use functions within our object. In simple, we can do this following:
javascript Code
var cloneOfA = JSON.parse(JSON.stringify(a));

This works for all kind of objects containing objects, arrays, strings, booleans and numbers.

READ  [ Solved - 11 Answers ] JQUERY - JAVASCRIPT - Abort Ajax requests using jQuery

  • In ECMAScript 6 there is Object.assign method, which copies values of all enumerable own properties from one object to another.

For example:

javascript Code
var x = {myProp: "value"};
var y = Object.assign({}, x); 

But be aware that nested objects are still copied as reference.

  • To mention Object.create from ECMAScript 5, which definitely does not give an exact copy, but sets the source as the prototype of the new object.
  • Case1:
    Where such inheritance is useful.
  • Case2:
    Where the source object won’t be modified, thus making the relation between the 2 objects a non issue.

Example:

javascript Code
var wiki = { a : 1 };
var techy = Object.create(wiki);
wiki.a; // 1
techy.a; // 1
wiki.a = 2;
techy.a; // 2 - prototype changed
techy.a = 3;
wiki.a; // Still 2, since setting techy.a makes it an "own" property

  • An elegant way to clone a Javascript object in one line of code
  • An Object.assign method is part of the ECMAScript 2015 (ES6) standard.
javascript Code
var clone = Object.assign({}, obj);

  • The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object.
  • The polyfill to support older browsers:
javascript Code
if (!Object.assign) {
  Object.defineProperty(Object, 'assign', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(target) {
      'use strict';
      if (target === undefined || target === null) {
        throw new TypeError('Cannot convert first argument to object');
      }
javascript Code
 var to = Object(target);
      for (var i = 1; i < arguments.length; i++) {
        var nextSource = arguments[i];
        if (nextSource === undefined || nextSource === null) {
          continue;
        }
        nextSource = Object(nextSource);

        var keysArray = Object.keys(nextSource);
        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
          var nextKey = keysArray[nextIndex];
          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
          if (desc !== undefined && desc.enumerable) {
            to[nextKey] = nextSource[nextKey];
          }
        }
      }
      return to;
    }
  });
}

  • Here the underscore.js library has a clone method.
javascript Code
y = _.clone(x);

or we can extend it like

javascript Code
copiedObject = _.extend({},originalObject);

  • To use JSON encoding to make deep copies of objects that do not have member methods.
  • The method is to JSON encode our target object, then by decoding it, we get the copy of the objects.
  • If we want many copies of the objects then we can decode as many times.
    Obviously, functions do not belong in JSON, so this only works for objects without member methods.
  • This method was perfect for our use case, since we are storing JSON blobs in a key-value store, and when they are exposed as objects in a JavaScript API, each object actually contains a copy of the original state of the object so we can calculate the delta after the caller has mutated the exposed object.
javascript Code
var object1 = {key:"value"};
var object2 = object1;

object2 = JSON.stringify(object1);
object2 = JSON.parse(object2);

object2.key = "a change";
console.log(object1);// returns value

  • Here is a function we can use the following:
javascript Code
function clone(obj) 
{
    if(obj == null || typeof(obj) != 'object')
        return obj;    
    var temp = new obj.constructor(); 
    for(var key in obj)
        temp[key] = clone(obj[key]);    
    return temp;
}

About the author

author

author

Wikitechy Founder, Author, International Speaker, and Job Consultant. My role as the CEO of Wikitechy, I help businesses build their next generation digital platforms and help with their product innovation and growth strategy. I'm a frequent speaker at tech conferences and events.

X