Skip to main content

What is the most efficient way to clone a JavaScript object?


What is the most efficient way to clone a JavaScript object? I've seen:




obj = eval(uneval(o));



but that's not cross platform (FF only). I've done (in Mootools 1.2) things like this:




obj = JSON.decode(JSON.encode(o));



but question the efficiency. I've also seen recursive copying function, etc. I'm pretty surprised that out-of-the-box JavaScript doesn't have a method for doing this.


Source: Tips4allCCNA FINAL EXAM

Comments

  1. I want to note that the .clone() method in jQuery only clones DOM elements. In order to clone JavaScript objects, you would do:

    // Shallow copy
    var newObject = jQuery.extend({}, oldObject);

    // Deep copy
    var newObject = jQuery.extend(true, {}, oldObject);


    More information can be found in the jQuery documentation.

    I also want to note that the deep copy is actually much smarter than what is shown above – it's able to avoid many traps (trying to deep extend a DOM element, for example). It's used frequently in jQuery core and in plugins to great effect.

    ReplyDelete
  2. There doesn't seem to be an in-built one, you could try:

    function clone(obj){
    if(obj == null || typeof(obj) != 'object')
    return obj;

    var temp = obj.constructor(); // changed

    for(var key in obj)
    temp[key] = clone(obj[key]);
    return temp;
    }


    There's a lengthy post with many contributing comments on Keith Deven's blog.

    If you want to stick to a framework, JQuery also has a clone() function:

    // Clone current element
    var cloned = $(this).clone();


    There were reported issues previously with this not working in Internet Explorer, but these were resolved as of version 1.2.3.

    ReplyDelete
  3. This is what I'm using:

    function cloneObject(obj) {
    var clone = {};
    for(var i in obj) {
    if(typeof(obj[i])=="object")
    clone[i] = cloneObject(obj[i]);
    else
    clone[i] = obj[i];
    }
    return clone;
    }

    ReplyDelete
  4. I know this is an old post, but I thought this may be of some help to the next guy who stumbles along.

    As long as you don't assign an object to anything it maintains no reference in memory. So to make an object that you want to share among other objects, you'll have to create a factory like so:

    var a = function(){
    return {
    father:'zacharias'
    };
    },
    b = a(),
    c = a();
    c.father = 'johndoe';
    alert(b.father);

    ReplyDelete
  5. In Prototype you would do something like

    newObject = Object.clone(myObject);


    The Prototype documentation notes that this makes a shallow copy.

    ReplyDelete
  6. Code:

    // extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned
    function extend(from, to)
    {
    if (from == null || typeof from != "object") return from;
    if (from.constructor != Object && from.constructor != Array) return from;
    if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function ||
    from.constructor == String || from.constructor == Number || from.constructor == Boolean)
    return new from.constructor(from);

    to = to || new from.constructor();

    for (var name in from)
    {
    to[name] = typeof to[name] == "undefined" ? extend(from[name], null) : to[name];
    }

    return to;
    }


    Test:

    var obj =
    {
    date: new Date(),
    func: function(q) { return 1 + q; },
    num: 123,
    text: "asdasd",
    array: [1, "asd"],
    regex: new RegExp(/aaa/i),
    subobj:
    {
    num: 234,
    text: "asdsaD"
    }
    }

    var clone = extend(obj);

    ReplyDelete
  7. Object.prototype.clone = function() {
    var newObj = (this instanceof Array) ? [] : {};
    for (var i in this) {
    if (i == 'clone') continue;
    if (this[i] && typeof this[i] == "object") {
    newObj[i] = this[i].clone();
    } else newObj[i] = this[i]
    } return newObj;
    };

    ReplyDelete
  8. function clone(obj)
    { var clone = {};
    clone.prototype = obj.prototype;
    for (property in obj) clone[property] = obj[property];
    return clone;
    }

    ReplyDelete
  9. Crockford suggests (and I prefer) using this function:

    function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
    }

    var newObject = object(oldObject);


    It's terse, works as expected and you don't need a library.

    ReplyDelete
  10. There seems to be no ideal deep clone operator yet for array-like objects. As the code below illustrates, John Resig's jQuery cloner turns arrays with non-numeric properties into objects that are not arraays, and RegDwight's JSON cloner drops the non-numeric properties. The following tests illustrate these points on multiple browsers:

    function jQueryClone(obj) {
    return jQuery.extend(true, {}, obj)
    }

    function JSONClone(obj) {
    return JSON.parse(JSON.stringify(obj))
    }

    var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]];
    arrayLikeObj.names = ["m", "n", "o"];
    var JSONCopy = JSONClone(arrayLikeObj);
    var jQueryCopy = jQueryClone(arrayLikeObj);

    alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) +
    "\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) +
    "\nWhat are the arrayLikeObj names? " + arrayLikeObj.names +
    "\nAnd what are the JSONClone names? " + JSONCopy.names)

    ReplyDelete
  11. @David why not just use:

    var newObject = JSON.parse(JSON.stringify(oldObject));


    ?

    ReplyDelete
  12. The way you are supposed to do it in Mootools.

    var my_object = {one:1,two:2, subobject:{a:['a','A']}},three:'3'};
    var my_object_clone = $merge({},my_object);

    ReplyDelete
  13. If you're using it, the underscore.js library has a clone method.

    var newObject = _.clone(oldObject);

    ReplyDelete
  14. function deepClone(obj, CloneObj) {
    CloneObj.clear();
    jQuery.each(obj, function(i, val) {
    var newObject = jQuery.extend(true, {}, val);
    CloneObj[i] = newObject;
    });
    }

    ReplyDelete
  15. in my FF3.6/IE8/Chrome4 works only this solution:

    function cloneObject(obj){
    var newObj = (obj instanceof Array) ? [] : {};
    for (var i in obj) {
    if (obj[i] && typeof obj[i] == "object")
    newObj[i] = obj[i].clone();
    else
    newObj[i] = obj[i];
    }
    return newObj;
    }


    I don't know why, but Object's prototype extension doesn't work well in FF ;(

    ReplyDelete
  16. // obj target object, vals source object

    var setVals = function (obj, vals) {
    if (obj && vals) {
    for (var x in vals) {
    if (vals.hasOwnProperty(x)) {
    if (obj[x] && typeof vals[x] === 'object') {
    obj[x] = setVals(obj[x], vals[x]);
    } else {
    obj[x] = vals[x];
    }
    }
    }
    }
    return obj;
    };

    ReplyDelete
  17. for mootools in particular this severs the reference between the new obj and the old one:

    var obj = {foo: 'bar'};
    var bar = $unlink(obj);


    you can also do

    var obj = {foo: 'bar'};
    var bar = $merge({}, $obj);


    although $merge uses $unlink anyway.

    p.s. for mootools 1.3 this becomes Object.clone

    ReplyDelete
  18. I used this clone method: http://oranlooney.com/static/javascript/deepCopy.js

    ReplyDelete
  19. although the question is closed, i think that this is the best solution if you want to generalize you object cloning algorithm.
    It can be used with or without jquery, although i recommend leaving jquery's extend method out if you want you cloned object to have the same "class" as the original one.

    function clone(obj){
    if(typeof(obj) == 'function')//it's a simple function
    return obj;
    //of it's not an object (but could be an array...even if in javascript arrays are objects)
    if(typeof(obj) != 'object' || obj.constructor.toString().indexOf('Array')!=-1)
    if(JSON != undefined)//if we have the JSON obj
    try{
    return JSON.parse(JSON.stringify(obj));
    }catch(err){
    return JSON.parse('"'+JSON.stringify(obj)+'"');
    }
    else
    try{
    return eval(uneval(obj));
    }catch(err){
    return eval('"'+uneval(obj)+'"');
    }
    // I used to rely on jQuery for this, but the "extend" function returns
    //an object similar to the one cloned,
    //but that was not an instance (instanceof) of the cloned class
    /*
    if(jQuery != undefined)//if we use the jQuery plugin
    return jQuery.extend(true,{},obj);
    else//we recursivley clone the object
    */
    return (function _clone(obj){
    if(obj == null || typeof(obj) != 'object')
    return obj;
    function temp () {};
    temp.prototype = obj;
    var F = new temp;
    for(var key in obj)
    F[key] = clone(obj[key]);
    return F;
    })(obj);
    }

    ReplyDelete
  20. This isn't generally the most efficient solution, but it does what I need. Simple test cases below...

    function clone(obj, clones) {
    // Makes a deep copy of 'obj'. Handles cyclic structures by
    // tracking cloned obj's in the 'clones' parameter. Functions
    // are included, but not cloned. Functions members are cloned.
    var new_obj,
    already_cloned,
    t = typeof obj,
    i = 0,
    l,
    pair;

    clones = clones || [];

    if (obj === null) {
    return obj;
    }

    if (t === "object" || t === "function") {

    // check to see if we've already cloned obj
    for (i = 0, l = clones.length; i < l; i++) {
    pair = clones[i];
    if (pair[0] === obj) {
    already_cloned = pair[1];
    break;
    }
    }

    if (already_cloned) {
    return already_cloned;
    } else {
    if (t === "object") { // create new object
    new_obj = new obj.constructor();
    } else { // Just use functions as is
    new_obj = obj;
    }

    clones.push([obj, new_obj]); // keep track of objects we've cloned

    for (key in obj) { // clone object members
    if (obj.hasOwnProperty(key)) {
    new_obj[key] = clone(obj[key], clones);
    }
    }
    }
    }
    return new_obj || obj;
    }


    Cyclic array test...

    a = []
    a.push("b", "c", a)
    aa = clone(a)
    aa === a //=> false
    aa[2] === a //=> false
    aa[2] === a[2] //=> false
    aa[2] === aa //=> true


    Function test...

    f = new Function
    f.a = a
    ff = clone(f)
    ff === f //=> true
    ff.a === a //=> false

    ReplyDelete
  21. This is the fastest method I have created that doesn't use the prototype, so it will maintain hasOwnProperty in the new object. The solution is to iterate the top level properties of the original object, make 2 copies, delete each property from the original and then reset the original object and return the new copy. It only has to iterate as many times as top level properties. This saves all the if conditions to check if each property is a function/object/string etc, and doesn't have to iterate each descendant property. The only drawback is that the original object must be supplied with its original created namespace, in order to reset it.


    copyDeleteAndReset:function(namespace,strObjName){
    var obj = namespace[strObjName],
    objNew = {},objOrig = {};
    for(i in obj){
    if(obj.hasOwnProperty(i)){
    objNew[i] = objOrig[i] = obj[i];
    delete obj[i];
    }
    }
    namespace[strObjName] = objOrig;
    return objNew;
    }

    var namespace = {};
    namespace.objOrig = {
    '0':{
    innerObj:{a:0,b:1,c:2}
    }
    }

    var objNew = copyDeleteAndReset(namespace,'objOrig');
    objNew['0'] = 'NEW VALUE';

    console.log(objNew['0']) === 'NEW VALUE';
    console.log(namespace.objOrig['0']) === innerObj:{a:0,b:1,c:2};

    ReplyDelete
  22. In YUI you can do Deep object/array copy by

    //For Safe clone.. in case where you need to delete items on the cloned objects
    clonedObj = Y.Clone(obj, true);

    //For Unsafe clone
    clonedObj = Y.Clone(obj, false);


    More details

    ReplyDelete
  23. I recently came up with this clone function. It's very simple, and does not have any external dependencies. As you can see it basically buries the original object within the function, and forks it into two new instances of itself.

    function clone(objectToClone) {
    var myClone = function() {};
    myClone.prototype = objectToClone;
    return [
    new myClone(),
    new myClone()
    ];
    }


    Keep in mind however that this is only a shallow clone. Objects and arrays that are set to properties of the original object will still contain references to those same objects and arrays in the new object. Replacing any root level properties in one of the objects however will not affect the other in any way.

    function foo() {}
    foo.prototype.runTest = function() {
    alert(this.test);
    };

    var bar = new foo();
    bar = clone(bar); // forks bar into two separate copies of bar
    bar[0].test = 'this';
    bar[1].test = 'that';

    bar[0].runTest(); // alerts "this" as expected
    bar[1].runTest(); // alerts "that" as expected


    Now if only I could find a way to replace the original cloned object with a new instance of itself within the clone function, this would be the ultimate js clone function. Until then, perhaps the function should be called fork rather than clone.

    ReplyDelete
  24. Has anyone tried this?

    Object.clone = function ()
    {
    var ClonedObject = function(){};
    ClonedObject.prototype = this;
    return new ClonedObject;
    }


    It seems to work and I can't see what pitfalls would be.
    In my tests the cloned object is instanceof the correct objects.

    Note: it could also be implemented as a standalone function, i.e.

    function clone(object)
    {
    // (replace "this" with "object")
    ...
    }

    ReplyDelete

Post a Comment

Popular posts from this blog

[韓日関係] 首相含む大幅な内閣改造の可能性…早ければ来月10日ごろ=韓国

div not scrolling properly with slimScroll plugin

I am using the slimScroll plugin for jQuery by Piotr Rochala Which is a great plugin for nice scrollbars on most browsers but I am stuck because I am using it for a chat box and whenever the user appends new text to the boxit does scroll using the .scrollTop() method however the plugin's scrollbar doesnt scroll with it and when the user wants to look though the chat history it will start scrolling from near the top. I have made a quick demo of my situation http://jsfiddle.net/DY9CT/2/ Does anyone know how to solve this problem?

Why does this javascript based printing cause Safari to refresh the page?

The page I am working on has a javascript function executed to print parts of the page. For some reason, printing in Safari, causes the window to somehow update. I say somehow, because it does not really refresh as in reload the page, but rather it starts the "rendering" of the page from start, i.e. scroll to top, flash animations start from 0, and so forth. The effect is reproduced by this fiddle: http://jsfiddle.net/fYmnB/ Clicking the print button and finishing or cancelling a print in Safari causes the screen to "go white" for a sec, which in my real website manifests itself as something "like" a reload. While running print button with, let's say, Firefox, just opens and closes the print dialogue without affecting the fiddle page in any way. Is there something with my way of calling the browsers print method that causes this, or how can it be explained - and preferably, avoided? P.S.: On my real site the same occurs with Chrome. In the ex