If you’re reaching for structuredClone, what you really want is native immutable Record and Tuple syntax, and the companion “deep path properties” syntax which allows for efficient and ergonomic immutable updates:
I dream of a world where ClojureScript became the common front end language of the web instead of JS and what that would have done for the language and ecosystem.
There are other reasons for cloning objects apart from immutable updates. Might also be you just want to pass around an object reference and make sure it is not mutated later, so you use a clone.
And structuredClone was standardized to serialize objects passed between contexts.
So I don't see what's wrong with teaching people about this as the native way to clone serializable objects.
Record/Tuples are different types and I don't think that "what you really want" is a future proposal when you want a built-in now.
Yeah it is really easy to fall into a misuse of expensive object memoization with deep equality checks if you try to "do it right" in React for example, and work without an external state management library.
Or when working with libraries that demand this.
In that spirit, I find MobX pleasant to work with as an alternative.
Although it remains needed to grok reference equality checks in reactivity, there's no way around that I guess.
Fundamentally, when passing messages with structured payloads I'd consider it good style for the payload to be serializable and free from references and mutable state.
While I am extremely excited that these may someday make it into the language, I actually would be fine with `.hashCode` and `.equals` on the Object prototype.
structuredClone, if I'm not mistaken also allows cloning functions/Maps/Sets, so while there may be some overlap, I'm not sure having Records and Tuples solves the same problems. Or as nerdy engineers are so prone to say, they're "orthogonal".
I know Reflect[0] doesn't get alot of attention however I always thought it would be a big improvement if `Reflect.set`, `Reflect.get` and `Reflect.deleteProperty` gained support to deal with nested properties. It would effective give developers built-in JSON Path support[1] (like you see in MySQL, Postgres or SQLite databases).
As an unrelated aside, recursive proxies would also be extremely useful
I'm not a fan of it there either. JavaScript, since its earliest days, had `private` as a reserved word. Typescript makes use of it. It conveys perfectly what the Class member's visibility should be. Using `#` might be easier on parsers, or may fix some sort of compatibility issue (I haven't read the reasoning) but it's silly.
There's a lot of reasoning for it, mostly summarized by "this.abc and this.#abc are not the same fields".
The goal of #-private properties in JS is to have total runtime isolation, so that there's no way at all to access it from the outside. If you're fine with a compile-time error, use TypeScript's private keyword instead (JS does not have a compiler).
But why can't `otherObj.abc = 3` throw an error if abc is declared as private? Because it would incur a runtime cost on all property accesses to check whether it's public or not, even if private properties aren't used anywhere within the class. Not a sacrifice anyone would be willing to make.
So, the solution is to make sure that private property accesses can be distinguished from public property accesses. You could have had something like `private.abc` instead of `this.#abc`. But I don't think that's better, either.
Honestly, you'll get used to # if you use it for a while.
This is similar to the recent post about private members in Ruby. It's a sytaxical check that the receiver is literally 'this'. Which, like you say, can be checked at parse time without any lookups. Even identity(this).privateVar doesn't pass in Ruby due to not being sytaxical this.
TIL, thanks. But uh, I expected `JSON.stringify` to throw errors when met with unencodable objects, like Python does. But it silently corrupts the values. A typical JavaScript thing.
I also think the automatic conversion of a `Date` to a string is a bad thing, which again is prohibited in Python.
It doesn't "corrupt" them; it converts them to JSON strings. It's in the name. Expecting `JSON.stringify` to throw in those situations would be like expecting `str` in Python to throw.
What irks me is its default behavior. A function should do safe operations by default. Compared to `JSON.stringify()`, 'structuredClone()` throws an error when it encounters values that cannot be cloned, which is a much saner approach. Probably because they learned from the mistake.
I understand your point and I can’t deny that Javascript continues to introduce weird, silent failures and quirks even today when everything is a bit more thought out than “the bad old days”.
But I think in the case of JSON.stringify it’s more about use case. 99% of the time, users of this method are taking some data and serialising it to a JSON compliant form to send to the server. JSON doesn’t support functions, or complex objects like a Date, so I tend to think it’s a reasonable default that functions disappear and Date’s are converted to an ISO standard. To insist that every single user runs a preparation step that strips out unserialisability data and chooses how to handle Date objects sounds laborious, error prone, and ripe for another npm dependency everyone suddenly normalises for every project.
Maybe a “strict mode” of some sort where you could have it throw on anything for cases where you need to guarantee everything is being sent?
OTOH, I have to concede that while this method has silent failures, they then implemented JSON.parse to throw at the slightest issue. So I have to admit there’s consistency even within the API.
I guess there's some good reason nobody ever did it, but what about throwing an UnhandledType kind of error and letting the catch() decide how to deal with the object in question?
On top of the compatibility issue mentioned in sibling comment, the existing behavior also matches the principle of keeping simple things simple without making complicated things impossible. If you want stringify with a check for unhandled elements, that's easily specified in a replacer (that does not really replace).
You might prefer some well established standard implementation over ad hoc roll your own, but that's a discussion about npm culture, not about stringify.
The problem I see with this is that whatever you’re sending this to must have knowledge of the meta information superjson produces, so at that point you’re investing in it as a wrapper library. The fact you can extend the types it serialises also complicates things and means the receiver needs further implementation specific knowledge.
I think in my original comment, I was imagining a world where JSON.serialize threw errors on unknown types and we needed a wrapper just get basic JSON out of it.
`str` is the equivalent of `toString`, not of `JSON.stringify`. Failure of a serializer-deserializer to roundtrip properly is corruption. The poorly chosen name (which is usually called "dump" or "serialize" in other languages) does not give license for silent corruption.
That is the solution. It allows JSON.serialize to exhibit the non-corrupting behaviour, giving a reasonable indication to the developer which is which by the name alone.
The issue, if there is one, is that nobody ever got around to implementing JSON.serialize.
If a JSON.serialize function did exist, would it do the same thing as JSON.stringify, just with catchable exceptions when one tries to serialize something that can't be (e.g. functions, date objects)?
It depends, it does 'corrupt' Sets and Maps. But it can throw errors on other values. For example it will throw when a field has a value of undefined, or when there are circular references.
As a JS/TS dev since the early node says I still can't believe how long we used the json stringify method for.
Not because it worked well, but because it was _good enough_. I feel like that in itself is an important lesson about our industry and probably the world.
It reminds me of using the STUFF() + FOR XML PATH trick in T-SQL (mssql) for so many years to aggregate row values into a comma separated string. Now you just call STRING_AGG()
For data to rendering on UI, JSON.parse(JSON.stringify()) is enough.
For other operations, clone an object with such complex structure is not a good idea, you may change your way of coding.
Agreed, generally. But it may be nice for saving complicated game states, or sending complicated objects to a Worker without having to rebuild them from JSON.
Wow, that's nostalgic. I remember implementing and writing about deepCopy() in JavaScript over 14 (!) years ago[1]. If you read the OP article and were left wondering how such an algorithm actually works, that old article explains it in some detail. The basic idea is still the same; only the handling of edge cases introduced by newer language features is different.
That implementation eventual became obsolete (although it still runs on basic object types) because of newer features being added to the JavaScript language, and by then other implementations were available. It's great to hear a de facto standard is emerging, even if hasn't made it into ECMAScript standard yet[2]. It sounds like the same edge cases are still causing problems. I think if a language is to ever offer really great support for deep copy it must be designed into the language as a first-class feature from the get go.
Well it sounds like it’s a web standard and clones a heck ton of browser objects that ECMAScript wouldn’t be able to require it to clone. But yes, I suppose requiring it to exist and work on non-host objects in ECMAScript would be ideal!
I wasn't aware of this, I would like to give a shout out to superjson (https://github.com/blitz-js/superjson) which I had been using to solve this problem. I will look at this solution for next time.
Actually what you linked looks superior in be case of: client + server. If I need to send objects over the wire that have Maps and other properties, that appears to be a better method.
> VM187:1 Uncaught DOMException: Failed to execute 'structuredClone' on 'Window': () => {} could not be cloned.
Ah, yes. I actually asked this question on StackOverflow 5 years ago [0], and the reason it can't be cloned isn't the worst reason ever... but it basically breaks cloning any non-trivial object. Well, I suppose I'll check back in another 10 years when the next ecmascript proposal around cloning lands...
It's not an object. JSON wouldn't cover that. It's code. With code complexity. That's non trivial & you seem to double down on an extensive ask far beyond .odt people's & what the typical need is, in a way that belittles the effort. I don't like how short you are, how you phrase this as some huge weakness. When in fact JSON.stringify/parse - what everyone uses today - have the same object-but-not-code limitations.
It's not just an object though. It's an object that has potentially has implicit bindings to something in the rest of the runtime. Bindings that (to my lament) aren't visible in the first clas-object; secret hidden internal state, tying it to the rest of the runtime.
JSON defines what is possible to do that doesn't involve arbitrary bindings to the rest of the runtime. Short of serializing the world, JSON is the best we can do.
It would be possible to try to serialize, and maybe a function is simple enough to serialize. Do we have existing examples of this? There's hundreds of different serialization libraries. For some reason everyone in this thread is super ignoring what seems like a basic widespread limitation, is being condescending to structuredClone, for not doing what no one else does either. Because there's a very good reason: because here be dragons. I don't get why everyone is so combative & aggressive over what seems so clear.
Why are you talking about JSON? This was a discussion about structuredClone.
> I don't like how short you are, how you phrase this as some huge weakness.
I think you may be reading into my comments more than what is actually there. I think it's a reasonable enough compromise, and I'm happy to have structuredClone.
It's been a little while since I wrote JavaScript. Aren't Function objects immutable? Would it not satisfy the desired behavior of clone to reference the same function (not copy it) in the new object?
Even if functions were immutable as objects, they can reference anything within their containing scope, including arbitrary dynamic variables in outer scopes and even values from other modules (whose bindings are “live”). They may also have their “this” bound to arbitrary objects from any scope at all.
Function objects are mutable in the sense that you can define new properties for them or assign new values to (writable) existing properties. This is sometimes used to simulate static variables:
I just did a deep dive into this (pun intended). The name appears to date back well over a decade (probably longer) and has roots in lower-level browser APIs like the implementation of `postMessage`.
> Structured cloning algorithm defines the semantics of copying a well-defined subset of ECMAScript objects between Code Realms. This algorithm is extensible by host enviroment to support cloning of host objects.
Eventually, in 2015, it was suggested to expose the `structuredClone` algorithm as an API:
> Has anyone ever proposed exposing the structured clone algorithm directly
as an API? Katelyn Gadd was musing about fast deep copy in JS on Twitter,
and I proposed a hack to use postMessage to do so[1], which works but it's
a little roundabout. Since structured clone is a primitive that the web
platform is built on it seems like a primitive that ought to be exposed. I
know this exists in other languages (Python has copy.deepcopy[2]) and
there's an npm "deepcopy" module[3] with lots of downloads so this is
clearly something people use.
I was trying to find something about that, but wasn't able to. I would be very curious if someone else can find meeting minutes or mailing list posts where they discuss it.
It does, but in fairness we are talking about the second hardest problem in computer science. I, for one, prefer it to deepCloneKindOfButNotReallyWatchOutForTheCasesThatMightSupriseYou.
"Structured clone" is enough to give you pause and question "what could structured mean?" (just as you did), at which point you will read the docs and find out. It succeeds in communicating what it needs to.
Oh, no, I didn't really question "what could structured mean", nor do the docs explain what "structured" means (https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers...), so what exactly does it succeed in communicating? One thing it succeeds in hiding is the first thing mentioned in the docs - "method creates a deep clone" (funnily enough, linking to an article more appropriately called "Deep copy")
What gave me a pause (and would be part of "yet another bump on the road to intuitive use") is the disconnect between the awareness of the better naming and the choice of a worse one (so, in a sense, this hardest problem has already been solved by other computer scientists!)
The actual docs do. This second-party attempt to recreate the docs in their own way that you point to has fallen short, I agree. Granted, Mozilla does a better job than some of the other second-party recreations, like Deno's. There is little question that the Javascript ecosystem is a bit of a disaster.
That said, I clearly see the caveats right there up front and centre, so even still it has successfully managed to provide you with what the function name wants to bring to your attention.
A lot of Lodash functions are implemented as combinations of other Lodash functions, so importing a single function actually imports half of Lodash under the hood:
Unnecessary _now_ perhaps, but not unnecessary when lodash was written. In 2009, when underscore.js came out (from which lodash was a fork) Array.every() was not something you could rely on. Chrome just came out a year earlier (and didn't support Array.every until version 4) and half the world was on IE 6-7-8
The expectation is that you import the whole library and not just one function. At which point, lodash is smaller than if each function was implemented separately.
I actively fight for removal of lodash in every frontend codebase I encounter. The cost is just too much. Often I run into developers who tell me there's some sort of webpack plugin to treeshake all the `import _ from 'lodash';` nonsense. The truth is, when we run a bundle analyzer, it's still one of the biggest bloaters. Webpack is also becoming less common in newer codebases. The point is, while it provides a ton of convenience methods, there is a cost.
structuredClone is a standard library implementation of the structured clone algorithm, which was originally mainly designed for passing data between realms (eg between threads). Prototype chains are not preserved across realms, so it makes sense that the algorithm wouldn’t either. The accommodation for native/host types is possible because they’re implemented in each realm by the runtime, but user-defined classes don’t have that benefit.
On the one hand, you’d think the copy would just have a prototype that points to the same as the original’s prototype, and that would make this all work out. I’m surprised that’s not what happens since in 99% of cases it would be fine.
On the other hand, when you consider #privateMembers, you realize cloning a class in JS is basically impossible (and adding them to the language was probably a horrible mistake).
It is very frequently needed when you're working with a component framework like React or Vue. Typically leaf components shouldn't mutate properties directly but rather emit a changed version of the original data they receive.
But it's not necessarily related to frameworks; if you're working with complex enough data structures and you're following a functional approach, you'll need to do similar things sooner than later.
I'm not sure if you're asking why deep copies are useful or something else.
Maybe you're handing over your data to a library developed by someone else and you want to make sure the library cannot mutate your original data, so you opt to pass a deep copy instead.
Or maybe you are the author of said library and you want to make sure you preserve the original data so you copy it on input rather than on output.
There are many situations where deep-copying is useful but I agree that you should use the simplest pattern that works for your use-case.
if "current" is a deep object here and contains other objects/arrays, you risk that wherever you are sending this shallow copy will mutate those deeper values and potentially mess things up for the code where "current" came from.
Maybe it's not a situation that comes up often, but it would be fairly hard to debug and guarding yourself against mysterious problems in advance is always neat.
You could make the same argument backwards though - many people may find it easier to do deep copies rather than throwing extra software they might not necessarily be familiar with.
When the caller passes you a deep structure and you want to ensure they don't mutate it afterwards. But I agree, it's seldom needed in application code.
I came across a bug recently where a data structure from Redux, which is immutable, was passed to a GraphQL library that would modify the data it was passed. So we had to make a deep clone.
I have never needed to "deep clone" an object in JavaScript. Any time I thought I needed to do that, it was actually a code smell for a different underlying problem.
I was glad to see the end of the "immutable everything" dogmatic obsession wrought by Redux. The code smell in that case was usually related to passing entire objects as props.
Can anyone describe a compelling use case for "deep cloning" objects in JavaScript (keep in mind _everything_ is
an object in JavaScript) that isn't dancing around some hidden complexity? IMO, if it can't be (de)serialized via JSON.stringify and JSON.parse, then you should consider why you're trying to serialize it in the first place, and why you're scared to pass around the reference to the object. (That said, structuredClone looks like a better alternative to the JSON method - but note it's still not cloning functions, so you should still be careful where/when you use it.)
The most common reason I've had to do it is to log objects to the console.
Chrome dev tools logs a pointer to the object, so if the object is mutated after logging, but before you view/expand it (especially for nested structures), it can be very confusing! JSON.parse(JSON.stringify()) usually is good enough for this :)
But yes, generally speaking I think a deep clone is a sign of a smell elsewhere. That doesn't mean it never has a place in any code, but just that if I find myself reaching for it, I usually see if there's a mistake being made elsewhere.
Isn't that desired? You want to see what the object looks at at the time of logging. If you need to see it after some additional code has run, you log it again.
to be honest, my experience with logging objects is so simple, i've never needed to try this live look into the object. my expectation was the log is a snap shot in time, not a live look at the object. an interactive view into the object does sound interesting rather than constant log entries for each step. which is how I would have tried to do something the first time only to be confused by it. TIL.
What the parent posts are trying to hint to you is that the default logging behaviour of the console is the live-look model, which is almost never what you wanted with logging. (Though it does have other uses.) To avoid the live-look, you can deep clone, or just stringify.
> Can anyone describe a compelling use case for "deep cloning" objects in JavaScript
Sure. A library accepts an object as function argument (for example ‘params’) and then uses it inside. Modifying users object in-place would be a bad idea.
Another issue is the opposite situation, where the library reads from the object multiple times, expecting the value to be stable, but the caller might change the value in between, either from the properties using getters or from the library being async. The library has to make a full deep copy to ensure that it's completely stable regardless of the caller's actions. Just freezing the input object would be confusing to the caller and still vulnerable to property getters.
Sure! Use a library that deep-clones the object first, leaving the original parameter unchanged! And now it's slower than the original library you're criticising.
If you know what modifications you need to make (hopefully the library author does), you only need to clone the relevant parts of the object (e.g. with spreading). Deep cloning still isn't usually necessary unless you're doing wierd stuff.
…the whole thread you’re replying to is about it not actually being necessary. To participate in this conversation you either accept that premise or disagree with it, passing it as a given is not really an option for the argument.
There are plenty of reasons to deepclone. That being said I do share some agreement. JavaScript imo is best when you limit complex structures and stick with primitives, ideally just one layer deep, but there are situations when it can’t be helped, e.g, dealing with api calls.
Yep. But API calls traverse the network, and assuming that's via JSON, you're already losing functionality like Date parsing, so the litmus test of JSON.stringify/parse seems appropriate to determine if you're dealing with a true "data object." If you run into issues with things like Dates, then it's a smell indicating a potential point of divergence in different codepaths that handle the network call differently.
Immutable structures are beautiful when I’ve got an editor for geographic data and want a high performance undo/redo stack with basically no effort. It’s also considerably cheaper to identify when groups of polygons have changed by object identity rather than deep equality. Did this property change? Did properties in general change? Did this polygon change? Did this collection of polygons change? All using === equality checks without any manual plumbing. About ten years into doing it this way and I’ve got one regret: starting with ImmutableJS instead of Immer.
If something is immutable then you never need to clone it. Just copy the reference.
If an object is immutable and you want to copy/mutate one item it then you can surgically clone it in O(how deeply nested the item to change is). Object spread being the simple case.
JS makes this messy to do though! Lots of spreads and array maps or you pull in a library like Immer and now have “coloured” objects. Immutability in JS is not idiomatic so for that reason I try to avoid unless I really need to.
I agree. I had this same problem with a tree UI model. Each node had links to both the children and the parent. Mutating any node in the tree, changes the reference of that node. Mutating the linkage to the updated node cascades to every other node in the tree.
I have an application with a big data structure built as a tree of objects. With deep cloning I can:
- Provide undo/redo. I just clone the whole tree.
- Load/Save from/to the database part of the tree. I load it in a separate object and then deep clone in place.
- Do mass modifications to a part of the tree. A clone a copy, apply modifications and the clone back in place.
I could do some of the changes in place, but to optimize Vue reactivity I work on copies.
Pedantically, while _everything_ is an object is true for Python it’s not for JavaScript which has distinct primitive types for strings, numbers, booleans, symbols, and undefined.
And null. I know that typeof(null) says 'object', but it's lying. When I say "it's lying", I mean that according to the spec, null is of type null. Also it has absolutely nothing in common with objects except for the output of typeof.
I am building a card game engine in typescript. A simple AI based on monte carlo tree search requires me to copy the gamestate a lot. And it's always a list of cards that i'm modeling so I really need to deepclone arrays.
Can you not serialize this state to/from JSON? I'd encourage trying to reduce the state to be JSON-serializable, and then re-initialize any "classes" after deserializarion with a .fromJSON() method (the "factory method" pattern) - in fact you can even call such .toJSON() "replacer" [0] and .fromJSON() "reviver" [1] functions in a callback passed to JSON.stringify and JSON.parse.
Deserializing requires paying all the same costs as deepcopying, both in terms of compute time and in terms of memory usage, but it also incurs the extra costs of parsing.
I'm not saying that there wouldn't be benefits to having a serializer for their gamestate (because there would surely be benefits), but I just cannot understand this as an alternative for the problem they described.
I haven't really gotten to the point where anything works but doing the serialize de/serialize combo is possible. The issue is, that I am thinking about doing this not a few hundred times but more like several million times (preferably per minute), so i'll likely have to implement several competing versions and benchmark them.
The full idea is implement a deckbuilding game like magic and use genetic algorithms and montecarlo simulation to balance the card pool. But that requires millions of games with millions of simulations to work. So i'll see if it's even possible.
If you really need maximum performance then you probably don't want to deal with JS objects at all. (But you should benchmark it first... you might be surprised how far V8 gets you with JIT optimizations - although serialization will be a bottleneck whether you're using JSON or deepClone.) For example, as one potential alternative, you could invent a scheme for encoding the state in a bit array, and then find clever bitwise operations for inspecting or manipulating it.
For inspiration, many chess engines store game state in a "bitboard" [0] which is a 64 bit representation of some (partial) state of the board.
Of course, at this point you might not even want to be using JavaScript... maybe you could write the hot path in Rust, compile it to wasm, and then use JS for orchestration/UI.
That is really useful, thank you! I think first stage is building the game and see if the whole Genetic Algorithm/Monte Carlo Setup actually works, even if the first few iterations take weeks.
The issue is I want to figure out if there are combos that I don't even know exist that are unusually strong. Basically make sure I balance the card pool but automatically.
There are at least two obvious alternatives to (fully) deepcloning.
First, many "everything must be immutable" libraries provide data structures that give operations that feel like mutations (but actually are not), and many of those can re-use all the old objects that are not mutated. This could save a lot, relative to full naive deepcopies. Or it could save very little, depending on what you're doing.
Second, if you have the ability, you can always mutate before recursion and then revert when the recursion finishes. This could potentially be very tricky, because it's not always easy to revert mutations. As such, it's not a good plan for a prototype, but if you can get it right, this could make your tree search wayyyyy faster.
You're probably aware of this, but in some cases you might be able to use SharedArrayBuffer [0], which doesn't make a copy of the underlying data but does use structured cloning to clone the outside of the object.
I only used it for experimentation so far, but I abondoned it, after I learned, that only one worker can be the active user of a SharedArrayBuffer. So they cannot be shared at the same time? Sharing seems to mean, now A uses it, than gives control back, via the main thread, then B uses it, etc
(but I hope I missunderstood something there)
But I need many threads to access the same data, at the same time (readonly, so no racecondition).
You might be mixing ArrayBuffer (which is a transferrable object[0] and gets zero-copy moved) and SharedArrayBuffer.
From [1]:
> The structured clone algorithm accepts SharedArrayBuffer objects and typed arrays mapped onto SharedArrayBuffer objects. In both cases, the SharedArrayBuffer object is transmitted to the receiver resulting in a new, private SharedArrayBuffer object in the receiving agent (just as for ArrayBuffer). However, the shared data block referenced by the two SharedArrayBuffer objects is the same data block, and a side effect to the block in one agent will eventually become visible in the other agent.
Caveat is you need these headers for security reasons (hence why the sandbox above has Express) or SharedArrayBuffer is not even defined (at least in Firefox):
An object containing just data for sure having lots of usecases. Like i.e undo/redo. But deep cloning would be as simple as json.parse(json.stringify(object))
Yeah, agreed. And in that case you should treat the "data object" as opaque, in which case it should be sufficient to pass it by reference. Hence, if you're reaching for deepClone, it's a code smell - why can't you pass the reference? Are you mutating the data object somewhere that you shouldn't be?
The saving grace here is that it destroys classes. I have often seen deep clone and deep equals as a sort of back-door API that then breaks when the implementation of the class does. The fact that this ignores prototypes makes it clear that you should only be using this for data, not "OO Objects".
React needs a new object for its hooks when you change them. We need to modify those objects, so deepClone + direct mutation on the clone bypasses a lot of annoying immutable object creation bs. ppl just destructure the whole thing anyways, which is actually also pretty slow
React hooks don't need to add the entire object to their dependency array; they can specify only the properties that are actually dependencies of the logic contained in the hook.
Not at all. It’s common to have nested data, it can be quite simple, but still nested. Think: config objects, simple state objects. I want my functions to be pure. Instead of modifying the nested object, they should return a modified deep copy.
This posts advice isn’t good because structuredClone performance is poor. Theres a small custom function I use for this which has much better performance.
If you’re so inclined you can google the benchmarks.
Sadly I can confirm, structuredClone is still not performant. At least on my various devices.
I just did a test again with 40000 times cloning a simple object and JSON.parse/stringify was 6 times faster than structured cloning.
Since I heavily use deep cloning, I am still disappointed with the new native, but slower solution. (And was hoping there would be more than old news in the article)
> I just did a test again with 40000 times cloning a simple object and JSON.parse/stringify was 6 times faster than structured cloning.
Depending on the object you're cloning, that might be fine. But some types, like Date or Set, don't round trip between JS and JSON so you'd have mangled objects.
Yes I know, but 6 times slower is pretty bad, so when someone presents the modern way, he should probably include a warning, that modern not necesarily means better. Because those who used the old way before, know the limits and worked around them. So why should I switch? I wasted time trying it out (not now, when structuredClone came out). Because my assumption was, it should be faster than the JSON abuse. But it is not, it just has more features.
- https://github.com/tc39/proposal-record-tuple
- https://github.com/tc39/proposal-deep-path-properties-for-re...