deep cloning of objects (v3)

Revision 3 of this benchmark created by derry on


Description

testing only DEEP cloning of objects

Preparation HTML

<script src="http://code.jquery.com/jquery-1.8.0.js" type="text/javascript"></script>
<script src="https://raw.github.com/bestiejs/lodash/master/lodash.min.js"></script>
<script>var lodash = _.noConflict();</script>

<script>
function deepClone1(obj) {
  return JSON.parse(JSON.stringify(obj));
}

function _deepClone2(obj) {
  var i, n, ret;
  if (obj instanceof Array)
  {
      ret = obj.slice();
      n = obj.length;
      for (i = 0; i < n; i++)
      {
        if (ret[i] instanceof Object)
          ret[i] = _deepClone2(ret[i]);
      }
      return ret;
   }
   else if (obj instanceof Date)
      return new Date(obj.getTime());
   ret = {};
   for (i in obj)
     ret[i] = obj[i] instanceof Object ? _deepClone2(obj[i]) : obj[i];
   return ret;
}

function deepClone2(obj) {
  if (!(obj instanceof Object))
    return obj;
  return _deepClone2(obj);
}

function deepClone2a(obj) {

  var id, idm, ret;

    // other:                                                                                                                                                          

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

    // array:                                                                                                                                                          

    if (Array.isArray(obj)) {
      ret = [];
      for (id = 0, idm = obj.length; id < idm; id++) {
          if (typeof obj[id] === "object") {
              ret.push(deepClone2a(obj[id]));
          } else {
              ret.push(obj[id]);
          }
      }

      return ret;

    }

    // object:                                                                                                                                                         

    ret = {};
    for (id in obj) {
        if (obj.hasOwnProperty(id)) {
            if (typeof(obj[id] === "object")) {
                ret[id] = deepClone2a(obj[id]);
            } else {
                ret[id] = obj[i];
            }
        }
    }

    return ret;

}

function deepClone2b(obj, type) {

    var i, idm, ret, ret2, typer, newType;

    typer = function typer(thing) {
        var result;
        if (Array.isArray(thing)) {
            result = 'array';
        } else if (typeof thing !== 'object' || thing === null) {
            result = 'other';
        } else {
            result = 'object';
        }

        return result;
    };

    if (!type) {
        type = typer(obj);
    }

    if (type === 'other') {
        return obj;
    }

    if (type === 'array') {
        ret = [];
        for (i = 0, idm = obj.length; i < idm; i++) {
            newType = typer(obj[i]);
            if (newType === 'other') {
                ret.push(obj[i]);
            } else {
                ret.push(deepClone2b(obj[i], newType));
            }
        }
        return ret;
    }

    // type === 'object'                                                                                                                                               

    ret = {};
    for (i in obj) {
        if (obj.hasOwnProperty(i)) {
            newType = typer(obj[i]);
            if (newType === 'other') {
                ret[i] = obj[i];
            } else {
                ret[i] = deepClone2b(obj[i], newType);
            }
        }
    }

    return ret;

}

// comes from: http://jsperf.com/cloning-an-object/23
function deepClone3(obj) {
    var target = {};
    for (var i in obj) {
      if (typeof(obj) === 'object') {
        target[i] = deepClone3(obj[i]);
      }
      else {
        target[i] = obj[i];
      }
    }
    return target;
  }

// adapted from: http://jsperf.com/cloning-an-object/23
function deepClone4(obj) {
   return lodash.clone(obj, /*deep=*/true);
}

// adapted from: http://jsperf.com/cloning-an-object/23
function deepClone5(obj) {
   return jQuery.extend(/*deep=*/true, {}, obj);
}

// comes from: http://jsperf.com/cloning-an-object/23
function deepClone6(obj) {
   return Object.clone(obj);
}

// adapted/reduced from: http://jsperf.com/structured-clone-objects/2 and https://developer.mozilla.org/en-US/docs/DOM/The_structured_clone_algorithm
function deepClone7(oToBeCloned) {
  if (oToBeCloned === null || !(oToBeCloned instanceof Object)) { return oToBeCloned; }
  var oClone, fConstr = oToBeCloned.constructor;
  oClone = new fConstr();
  for (var sProp in oToBeCloned) { oClone[sProp] = deepClone7(oToBeCloned[sProp]); }
  return oClone;
};


// ******************

// comes from: http://jsperf.com/cloning-an-object/23
  Object.defineProperties(Object, {
    'extend': {
      'configurable': true,
      'enumerable': false,
      'value': function extend(what, wit) {
        var extObj, witKeys = Object.keys(wit);

        extObj = Object.keys(what).length ? Object.clone(what) : {};

        witKeys.forEach(function(key) {
          Object.defineProperty(extObj, key, Object.getOwnPropertyDescriptor(wit, key));
        });

        return extObj;
      },
      'writable': true
    },
    'clone': {
      'configurable': true,
      'enumerable': false,
      'value': function clone(obj) {
        return Object.extend({}, obj);
      },
      'writable': true
    }
  });

// ******************

// ensure that the deep-clone is actually accurate
function compareObjs(obj1, obj2) {
  // snapshot how `obj1` currently looks
  var orig = JSON.stringify(obj1);

  // test if `obj2` really was a clone of `obj1`
  if (orig !== JSON.stringify(obj2)) {
    throw new Error("failed test 1");
  }

  // do some mutations to `obj2` clone
  obj2.b = "3";
  // check that `obj1` wasn't affected
  if (orig !== JSON.stringify(obj1)) {
    throw new Error("failed test 2");
  }

  obj2.c.push("foobar");
  // check that `obj1` wasn't affected
  if (orig !== JSON.stringify(obj1)) {
    throw new Error("failed test 3");
  }

  obj2.c[2].d++;
  // check that `obj1` wasn't affected
  if (orig !== JSON.stringify(obj1)) {
    throw new Error("failed test 4");
  }

  obj2.c[2].e = {
    f: "hello world"
  };
  // check that `obj1` wasn't affected
  if (orig !== JSON.stringify(obj1)) {
    throw new Error("failed test 5");
  }
}
</script>

Setup

var a = {
      b: "2",
      c: [3, 4,
      {
        d: 5,
        e: {
          f: {
            g: true
          }
        }
      }],
      h: {
        i: [{
          j: 1
        }]
      }
    };

Test runner

Ready to run.

Testing in
TestOps/sec
deepClone1 (JSON parse/stringify)
compareObjs(a, deepClone1(a));
ready
deepClone2 (full obj deep clone)
compareObjs(a, deepClone2(a));
ready
deepClone2a (based on deepClone2)
compareObjs(a, deepClone2a(a));
ready
deepClone2b (full obj deep clone)
// adapted from: http://jsperf.com/cloning-an-object/23

compareObjs(a, deepClone2b(a));
ready
deepClone5 (jQuery deep extend)
// adapted from: http://jsperf.com/cloning-an-object/23

compareObjs(a, deepClone5(a));
ready
deepClone6 (Object.clone)
// comes from: http://jsperf.com/cloning-an-object/23

// NOTE: this jsperf is intentionally set up to prove that the
// deep cloning shown on that other jsperf is invalid, that's
// why this test throws an error, because the comparison fails.

compareObjs(a, deepClone6(a));
ready
deepClone7 (structured clone)
// adapted/reduced from: http://jsperf.com/structured-clone-objects/2 
// and https://developer.mozilla.org/en-US/docs/DOM/The_structured_clone_algorithm

compareObjs(a, deepClone7(a));
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.

  • Revision 1: published by Kyle Simpson on
  • Revision 2: published by Bernie on
  • Revision 3: published by derry on
  • Revision 4: published by Marcin on
  • Revision 5: published by Marcin on
  • Revision 9: published by Oscar Campbell on
  • Revision 10: published by Oscar Campbell on
  • Revision 11: published on
  • Revision 12: published by Oscar Campbell on
  • Revision 28: published by cari bisnis rumahan sampingan on
  • Revision 29: published by cari modal usaha on
  • Revision 31: published on