JS Deep Copy

JavaScript performance comparison

Revision 11 of this test case created by Karl Waclawek

Preparation code

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.8.0/lodash.js"></script><script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>

      
<script>
Benchmark.prototype.setup = function() {
  function recursiveDeepCopy(o) {
    var newO,
      i;
  
    if (typeof o !== 'object') {
      return o;
    }
    if (!o) {
      return o;
    }
  
    if ('[object Array]' === Object.prototype.toString.apply(o)) {
      newO = [];
      for (i = 0; i < o.length; i += 1) {
        newO[i] = recursiveDeepCopy(o[i]);
      }
      return newO;
    }
  
    newO = {};
    for (i in o) {
      if (o.hasOwnProperty(i)) {
        newO[i] = recursiveDeepCopy(o[i]);
      }
    }
    return newO;
  }
  
  function jsonDeepCopy(o) {
    return JSON.parse(JSON.stringify(o));
  }
  
  function objectClone(o) {
  	var r, f;
  	if( o == null || typeof(o) != 'object' ) return o;
  	f = function() {}; f.prototype = o;
  	r = new f;
  	for( var i in o) r[i] = objectClone(o[i]);
  	return r;
  }
  
  function recursiveDeepCopy2(o) {
    var newO,  i;
  
    if (typeof o !== 'object') {  return o; }
    if (!o) {  return o;  }
  
    if ( o.constructor === Array ) {
      newO = [];
      for (i = 0; i < o.length; i += 1) {
        newO[i] = recursiveDeepCopy2(o[i]);
      }
      return newO;
    }
  
    newO = {};
    for (i in o) {
        newO[i] = recursiveDeepCopy2(o[i]);
    }
    return newO;
  }
  var mergeFast = (function() {
    function merge() {
      var result = arguments[0],
        deep = (result === true),
        size = arguments.length,
        item, index, key;
      if (deep || typeOf(result) !== 'object')
        result = {};
      for (index=1;index<size;++index)
        if (typeOf(item = arguments[index]) === 'object')
          for (key in item) {
            if (!item.hasOwnProperty(key))
              continue;
            result[key] = deep ? clone(item[key]) : item[key];
          }
      return result;
    }
    function clone(input) {
      var output = input,
        type = typeOf(input),
        index, size;
      if (type === 'array') {
        output = [];
        size = input.length;
        for (index=0;index<size;++index)
          output[index] = clone(input[index]);
      } else if (type === 'object') {
        output = {};
        for (index in input)
          output[index] = clone(input[index]);
      }
      return output;
    }
    function typeOf(input) {
      if (Array.isArray(input))
        return 'array';
      if (input !== null && input instanceof Object)
        return 'object';
    }
    return merge;
  })();
  
  function deepMerge3() {
  
      function merge(target, source) {
  
          for (var key in source) {
  
              // Delete any value passed as undefined
              if (typeof source[key] === 'undefined') {
                  delete target[key];
              }
  
              // Assign primitives, arrays and null directly to the target
              else if (typeof source[key] !== 'object' || source[key] instanceof Array || !source[key]) {
                  target[key] = source[key];
              }
  
              // Recursively integrate plain objects to the target
              else {
                  target[key] = merge(target[key] || {}, source[key]);
              }
          }
  
          return target;
      }
  
      return _.reduce(arguments, merge);
  }
  
  const deepCopy3 = (value) => { // deepCopy by Value
    let val;
    if (value && Array.isArray(value)) {
      val = [...value]; // Copy by Value instead of Reference - ES6 Spread
    } else if (value && typeof value === 'object') {
      val = { ...value }; // Copy by Value instead of Reference - ES6 Spread
    } else {
      return value; // Returns original value and stops!
    }
  
    Object.keys(val).forEach((key) => {
      const keyval = val[key];
      if (keyval ) {
        if (Array.isArray(keyval ) || (typeof keyval  === 'object')) {
          val[key] = deepCopy3(keyval ); // Recursion
        }
      }
    });
    return val; // Returns Final Value
  };
  
  
  
  var dimensions = [{
    "dimensions": [{
      "runtime": {
        "common": {
          "client": null,
          "server": null
        }
      }
    }, {
      "device": {
        "android": null,
        "blackberry": null,
        "iemobile": null,
        "iphone": null,
        "ipad": null,
        "kindle": null,
        "opera-mini": null,
        "palm": null
      }
    }, {
      "environment": {
        "development": {
          "dev": null,
          "test": null
        },
        "production": {
          "stage": null,
          "prod": null
        }
      }
    }, {
      "lang": {
        "ar": {
          "ar-JO": null,
          "ar-MA": null,
          "ar-SA": null,
          "ar-EG": null
        },
        "bn": {
          "bn-IN": null
        },
        "ca": {
          "ca-ES": null
        },
        "cs": {
          "cs-CZ": null
        },
        "da": {
          "da-DK": null
        },
        "de": {
          "de-AT": null,
          "de-DE": null
        },
        "el": {
          "el-GR": null
        },
        "en": {
          "en-AU": null,
          "en-BG": null,
          "en-CA": null,
          "en-GB": null,
          "en-GY": null,
          "en-HK": null,
          "en-IE": null,
          "en-IN": null,
          "en-MY": null,
          "en-NZ": null,
          "en-PH": null,
          "en-SG": null,
          "en-US": null,
          "en-ZA": null
        },
        "es": {
          "es-AR": null,
          "es-BO": null,
          "es-CL": null,
          "es-CO": null,
          "es-EC": null,
          "es-ES": null,
          "es-MX": null,
          "es-PE": null,
          "es-PY": null,
          "es-US": null,
          "es-UY": null,
          "es-VE": null
        },
        "fi": {
          "fi-FI": null
        },
        "fr": {
          "fr-BE": null,
          "fr-CA": null,
          "fr-FR": null,
          "fr-GF": null
        },
        "hi": {
          "hi-IN": null
        },
        "hu": {
          "hu-HU": null
        },
        "id": {
          "id-ID": null
        },
        "it": {
          "it-IT": null
        },
        "ja": {
          "ja-JP": null
        },
        "kn": {
          "kn-IN": null
        },
        "ko": {
          "ko-KR": null
        },
        "ml": {
          "ml-IN": null
        },
        "mr": {
          "mr-IN": null
        },
        "ms": {
          "ms-MY": null
        },
        "nb": {
          "nb-NO": null
        },
        "nl": {
          "nl-BE": null,
          "nl-NL": null,
          "nl-SR": null
        },
        "pl": {
          "pl-PL": null
        },
        "pt": {
          "pt-BR": null
        },
        "ro": {
          "ro-RO": null
        },
        "ru": {
          "ru-RU": null
        },
        "sv": {
          "sv-SE": null
        },
        "ta": {
          "ta-IN": null
        },
        "te": {
          "te-IN": null
        },
        "th": {
          "th-TH": null
        },
        "tr": {
          "tr-TR": null
        },
        "vi": {
          "vi-VN": null
        },
        "zh": {
          "zh-Hans": {
            "zh-Hans-CN": null
          },
          "zh-Hant": {
            "zh-Hant-HK": null,
            "zh-Hant-TW": null
          }
        }
      }
    }]
  }]

};
</script>

Preparation code output

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in CCBot 2.0.0 / Other 0.0.0
Test Ops/sec
jQuery deep extend
var dimensionsCopy = $.extend(true, {}, dimensions);
pending…
mergeFast
var dimensionsCopy = mergeFast.apply(null, dimensions);
pending…
objectClone
var dimensionsCopy = objectClone(dimensions);
pending…
deepCopy3
var dimensionsCopy = deepCopy3(dimensions);
pending…
Recursive2 Deep Copy
var dimensionsCopy = recursiveDeepCopy2(dimensions);
pending…
lodash clone
var dimensionsCopy = _.cloneDeep(dimensions);
pending…
deepMerge3
var dimensionsCopy = deepMerge3.apply(null,dimensions)
pending…
Recursive Deep Copy
var dimensionsCopy = recursiveDeepCopy(dimensions);
pending…
JSON Deep Copy
var dimensionsCopy = jsonDeepCopy(dimensions);
pending…

Compare results of other browsers

Revisions

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

0 Comments