Toggle navigation
MeasureThat.net
Create a benchmark
Tools
Feedback
FAQ
Register
Log In
Lodash cloneDeep vs structuredClone vs node-clone, cloning of typedarrays
(version: 0)
https://developer.mozilla.org/en-US/docs/Web/API/structuredClone
Comparing performance of:
Lodash cloneDeep vs Native structuredClone vs clone();
Created:
3 years ago
by:
Guest
Jump to the latest result
HTML Preparation code:
<script src='https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js'></script>
Script Preparation code:
var MyObject = { typed1: Int16Array.from(Array(500).fill(0).map((_, index) => (index % 20))), typed2: Int16Array.from(Array(1500).fill(0).map((_, index) => (index % 20))), typed3: Int16Array.from(Array(200).fill(0).map((_, index) => (index % 20))), typedRandom: Float32Array.from(Array(1500).fill(0).map(() => Math.random() * 200)), typedRandom2: Float32Array.from(Array(1500).fill(0).map(() => Math.random() * 200)), }; var myCopy = null; var clone = (function() { 'use strict'; function _instanceof(obj, type) { return type != null && obj instanceof type; } var nativeMap; try { nativeMap = Map; } catch(_) { // maybe a reference error because no `Map`. Give it a dummy value that no // value will ever be an instanceof. nativeMap = function() {}; } var nativeSet; try { nativeSet = Set; } catch(_) { nativeSet = function() {}; } var nativePromise; try { nativePromise = Promise; } catch(_) { nativePromise = function() {}; } /** * Clones (copies) an Object using deep copying. * * This function supports circular references by default, but if you are certain * there are no circular references in your object, you can save some CPU time * by calling clone(obj, false). * * Caution: if `circular` is false and `parent` contains circular references, * your program may enter an infinite loop and crash. * * @param `parent` - the object to be cloned * @param `circular` - set to true if the object to be cloned may contain * circular references. (optional - true by default) * @param `depth` - set to a number if the object is only to be cloned to * a particular depth. (optional - defaults to Infinity) * @param `prototype` - sets the prototype to be used when cloning an object. * (optional - defaults to parent prototype). * @param `includeNonEnumerable` - set to true if the non-enumerable properties * should be cloned as well. Non-enumerable properties on the prototype * chain will be ignored. (optional - false by default) */ function clone(parent, circular, depth, prototype, includeNonEnumerable) { if (typeof circular === 'object') { depth = circular.depth; prototype = circular.prototype; includeNonEnumerable = circular.includeNonEnumerable; circular = circular.circular; } // maintain two arrays for circular references, where corresponding parents // and children have the same index var allParents = []; var allChildren = []; var useBuffer = typeof Buffer != 'undefined'; if (typeof circular == 'undefined') circular = true; if (typeof depth == 'undefined') depth = Infinity; // recurse this function so we don't reset allParents and allChildren function _clone(parent, depth) { // cloning null always returns null if (parent === null) return null; if (depth === 0) return parent; var child; var proto; if (typeof parent != 'object') { return parent; } if (_instanceof(parent, nativeMap)) { child = new nativeMap(); } else if (_instanceof(parent, nativeSet)) { child = new nativeSet(); } else if (_instanceof(parent, nativePromise)) { child = new nativePromise(function (resolve, reject) { parent.then(function(value) { resolve(_clone(value, depth - 1)); }, function(err) { reject(_clone(err, depth - 1)); }); }); } else if (clone.__isArray(parent)) { child = []; } else if (clone.__isRegExp(parent)) { child = new RegExp(parent.source, __getRegExpFlags(parent)); if (parent.lastIndex) child.lastIndex = parent.lastIndex; } else if (clone.__isDate(parent)) { child = new Date(parent.getTime()); } else if (useBuffer && Buffer.isBuffer(parent)) { if (Buffer.from) { // Node.js >= 5.10.0 child = Buffer.from(parent); } else { // Older Node.js versions child = new Buffer(parent.length); parent.copy(child); } return child; } else if (_instanceof(parent, Error)) { child = Object.create(parent); } else { if (typeof prototype == 'undefined') { proto = Object.getPrototypeOf(parent); child = Object.create(proto); } else { child = Object.create(prototype); proto = prototype; } } if (circular) { var index = allParents.indexOf(parent); if (index != -1) { return allChildren[index]; } allParents.push(parent); allChildren.push(child); } if (_instanceof(parent, nativeMap)) { parent.forEach(function(value, key) { var keyChild = _clone(key, depth - 1); var valueChild = _clone(value, depth - 1); child.set(keyChild, valueChild); }); } if (_instanceof(parent, nativeSet)) { parent.forEach(function(value) { var entryChild = _clone(value, depth - 1); child.add(entryChild); }); } for (var i in parent) { var attrs = Object.getOwnPropertyDescriptor(parent, i); if (attrs) { child[i] = _clone(parent[i], depth - 1); } try { var objProperty = Object.getOwnPropertyDescriptor(parent, i); if (objProperty.set === 'undefined') { // no setter defined. Skip cloning this property continue; } child[i] = _clone(parent[i], depth - 1); } catch(e){ if (e instanceof TypeError) { // when in strict mode, TypeError will be thrown if child[i] property only has a getter // we can't do anything about this, other than inform the user that this property cannot be set. continue } else if (e instanceof ReferenceError) { //this may happen in non strict mode continue } } } if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(parent); for (var i = 0; i < symbols.length; i++) { // Don't need to worry about cloning a symbol because it is a primitive, // like a number or string. var symbol = symbols[i]; var descriptor = Object.getOwnPropertyDescriptor(parent, symbol); if (descriptor && !descriptor.enumerable && !includeNonEnumerable) { continue; } child[symbol] = _clone(parent[symbol], depth - 1); Object.defineProperty(child, symbol, descriptor); } } if (includeNonEnumerable) { var allPropertyNames = Object.getOwnPropertyNames(parent); for (var i = 0; i < allPropertyNames.length; i++) { var propertyName = allPropertyNames[i]; var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName); if (descriptor && descriptor.enumerable) { continue; } child[propertyName] = _clone(parent[propertyName], depth - 1); Object.defineProperty(child, propertyName, descriptor); } } return child; } return _clone(parent, depth); } /** * Simple flat clone using prototype, accepts only objects, usefull for property * override on FLAT configuration object (no nested props). * * USE WITH CAUTION! This may not behave as you wish if you do not know how this * works. */ clone.clonePrototype = function clonePrototype(parent) { if (parent === null) return null; var c = function () {}; c.prototype = parent; return new c(); }; // private utility functions function __objToStr(o) { return Object.prototype.toString.call(o); } clone.__objToStr = __objToStr; function __isDate(o) { return typeof o === 'object' && __objToStr(o) === '[object Date]'; } clone.__isDate = __isDate; function __isArray(o) { return typeof o === 'object' && __objToStr(o) === '[object Array]'; } clone.__isArray = __isArray; function __isRegExp(o) { return typeof o === 'object' && __objToStr(o) === '[object RegExp]'; } clone.__isRegExp = __isRegExp; function __getRegExpFlags(re) { var flags = ''; if (re.global) flags += 'g'; if (re.ignoreCase) flags += 'i'; if (re.multiline) flags += 'm'; return flags; } clone.__getRegExpFlags = __getRegExpFlags; return clone; })(); if (typeof module === 'object' && module.exports) { module.exports = clone; }
Tests:
Lodash cloneDeep
myCopy = _.cloneDeep(MyObject);
Native structuredClone
myCopy = structuredClone(MyObject);
clone();
myCopy = clone(MyObject);
Rendered benchmark preparation results:
Suite status:
<idle, ready to run>
Run tests (3)
Previous results
Fork
Test case name
Result
Lodash cloneDeep
Native structuredClone
clone();
Fastest:
N/A
Slowest:
N/A
Latest run results:
Run details:
(Test run date:
7 months ago
)
User agent:
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
Browser/OS:
Chrome 139 on Mac OS X 10.15.7
View result in a separate tab
Embed
Embed Benchmark Result
Test name
Executions per second
Lodash cloneDeep
2700.3 Ops/sec
Native structuredClone
154220.4 Ops/sec
clone();
1555.4 Ops/sec
Autogenerated LLM Summary
(model
llama3.2:3b
, generated one year ago):
It seems we have a benchmarking report! To summarize, there are three test cases: 1. `Lodash cloneDeep`: Uses the `_.cloneDeep` function from Lodash to create a deep copy of an object called `MyObject`. 2. `Native structuredClone`: Uses the built-in `structuredClone` function to create a deep copy of an object called `MyObject`. 3. `clone()`: Uses a custom `clone()` function to create a deep copy of an object called `MyObject`. The results are: * `Lodash cloneDeep`: 27976.2578125 executions per second * `Native structuredClone`: 1313.460693359375 executions per second ( approximately 1/21st the speed of Lodash) * `clone()`: 331.72052001953125 executions per second Based on these results, it appears that the custom `clone()` function is not as efficient as using the built-in `structuredClone` function or a well-tested library like Lodash. However, without more context about what's being cloned and why the performance differences matter, it's difficult to make any conclusions beyond this.
Related benchmarks:
Lodash cloneDeep vs. Lodash clone vs. Array.slice() vs. Array.slice(0) vs. Object.assign()
Lodash cloneDeep vs Lodash clone vs Array.splice() vs. Object.assign()
Lodash cloneDeep vs Lodash clone vs Array.splice() vs. Object.assign() vs Array.slice() vs Array.slice(0)
Lodash cloneDeep vs structuredClone, cloning of typedarrays
Comments
Confirm delete:
Do you really want to delete benchmark?