Cloning an Object (v41)

Revision 41 of this benchmark created on


Description

There is no quick and easy facility for cloning an object, Some people recommend using JQuery.extend others JSON.parse/stringify

http://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-clone-a-javascript-object

If you want the fastest possible clone function. I would personally anticipate the data structure of your object and write a custom clone to handle it.

Preparation HTML

<script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>

<script src="//underscorejs.org/underscore-min.js"></script>

<script>
  var oldObject = {
   a: 1,
   b: 2,
   c: 3,
   d: 4,
   e: 5,
   f: function() {
    return 6;
   },
   g: [7, 8, 9]
  };
  var i = 1000;
  while (i--)
    oldObject[Math.random()] = i;
  
  function clone(obj) {
   var target = {};
   for (var i in obj) {
    if (obj.hasOwnProperty(i)) {
     target[i] = obj[i];
    }
   }
   return target;
  }
var extend = function(source, target) {
    var keys = Object.keys(source),
        i = keys.length,
        target = target || {};
    while (i--) {
        target[keys[i]] = source[keys[i]];
    }
    return target;
}
    var fork = function(source) {
        var
        objmap = new Map(),
        targetOut = {__proto__: source.__proto__},
        target,
        stack = {0: {source: source, target: targetOut}},
        sp = 0,
        ss = 1,
        keys, key, j, scope, tmp;
        while ((scope = stack[sp++])) {
            keys = Object.keys(scope.source);
            j = keys.length;
            while (j--) {
                source = scope.source[key = keys[j]];
                if (source !== null && source === Object(source)) {
                    if ((tmp = objmap.get(source))) {
                        scope.target[key] = tmp;
                    } else {
                        scope.target[key] = target = {__proto__: source.__proto__};
                        stack[ss++] = {source: source, target: target};
                        objmap.set(source, target);
                    }
                } else {
                    scope.target[key] = source;
                }
            }
            delete stack[sp - 1];
        }

        target = source = stack = keys = key = scope = null;
        return targetOut;
    };




  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
   }
  });
</script>

Test runner

Ready to run.

Testing in
TestOps/sec
JQuery.extend deep
var newObject = jQuery.extend(true, {}, oldObject);
ready
JSON
var newObject = JSON.parse(JSON.stringify(oldObject));
ready
JQuery.extend
var newObject = jQuery.extend({}, oldObject);
ready
simple clone function
var newObject = clone(oldObject);
ready
ES5 Object.clone
var newObject = Object.clone(oldObject);
ready
efficient deep clone
var newObject = fork(oldObject);
ready
efficient extend
var newObject = extend(oldObject);
ready
undercore clone
var newObject = _.clone(oldObject);
ready
underscore extend
var newObject = _.extend({}, oldObject);
ready

Revisions

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