angular.copy from AngularJS 1.x is quadratic-slow on large objects, especially in IE.
JSON.parse for simple objects or
_.deepClone for objects with
Date objects or cyclic references. Actually, if you don't clone objects of more than 1000 nodes, this shouldn't bother you.
How I bumped into it
The other day I faced with a need to deeply clone an object from the redux store and since we're using angular I initially thought about angular.copy, but then I remembered, that its use is being discouraged because of "being very slow in IE".
Maybe, it was an old bug an now it's fixed? A quick search showed that there's an issue on github and it's still open.
OK, but how exactly slow is that function? People report it being slow on large objects.
I quickly created a generator of random objects of certain nesting depth and total node count, and a suite to test function. Here's a gist with everything combined. In order to measure one needs to run
suite with deep cloning function passed as an argument.
angular.copy turned out to be really slow. Even though it was OK for the case I started with, I went too far to stop researching the question. What other options do we have? Of course, I can write a stupid recursive cloner in ~10 lines of code and call it a day, but since I'm an ENTERPRISE developer and that day I didn't feel like writing my own solution, I considered existing libraries and really trivial methods, which could be written in a single line.
Actually, somewhere near the end of writing that post, I implemented simple cloner, and ... discovered nothing new. It's faster than much more elaborate
_.deepClone, but not that much. You can see a green line on the chart for the Chrome below.
So I stuck with the following four methods:
angular.copy— which I started from;
lodash.cloneDeep— a method from the most popular utility library. I think similar methods from other libs would work roughly the same;
- JSON — vanilla js, works surprisingly fast;
- utilizing structured clone via
postMessage— also vanilla js w/o serialization overhead.
postMessage w/o getting it back. That was enough to measure cloning process. More or less proper implementation is available in the same gist.
|method||cycles||non-trivial types||preserves prototypes||functions|
- non-trivial types is for
Dateand other built-in objects
- preserves prototypes is for user-defined custom objects
- 🛑 means that function fails with an exception instead of silently changing or throwing data away
Here are the charts. Average time in milliseconds by the number of nodes in an object.
- custom solution (link)
- CPU: i5-4210M @2.6GHz
- OS: Win10
- libraries: Angular 1.6.7, lodash 4.17.4
- Browsers: Chrome 63, Firefox 56, IE 11, Edge 14
I also checked
angular.copy in Safari on @sublimeye's Mac, but results are uninterestingly similar to Chrome apart from his CPU (i7-4770HQ @2.2GHz) is significantly faster than mine. Edge is almost the same as IE.
1 — documentation for
clone says that functions are transformed into empty objects and
cloneDeep works the same. But that's true only if we pass a function directly to the
cloneDeep. Any nested function property is just copied by a normal