What survives

Does obfuscation break Map and Set?

Collections look exposed under protection because they are keyed lookups, and keyed lookups are what renaming rewrites. Measured against the real engine in five configurations, Map, Set, WeakMap and structuredClone behave identically, and the underlying reason turns out to be a useful design tool rather than a coincidence.

Why collections look like they should break

A plain object and a Map both look like key-to-value lookups when you read the code. Since a protection pass can rewrite the keys of a plain object, it seems to follow that it might rewrite the keys of a Map too. If it did, a dispatch table keyed by command name would stop matching, and a cache keyed by identifier would start missing.

The concern gets sharper with WeakMap, which is often used to attach private state to an object that the code does not own, and with structuredClone, which copies a whole object graph in one call and would be an awkward place to discover a naming problem.

Measured, none of it moves. The reason is a genuine structural difference between the two kinds of lookup, and it is worth knowing because you can use it deliberately.

What was measured

One sample covered the surface: Map reads, writes and size; two distinct object keys that look identical but are separate entries; insertion order observed through forEach; the key normalisation rules under which NaN matches NaN and negative zero is stored as zero; delete and re-add moving an entry to the end; Set de-duplication, in which the number 1 and the string "1" stay distinct; WeakMap and WeakSet membership; a dispatch table whose values are functions; and a structuredClone of an object containing a nested array, a Map and a Set.

The file was run in Node, then protected and run again, with outputs compared line by line, across the default ES5 target, the modern target, a renaming build with identifier replacement and global renaming, the same on the modern target, and a string-table build with MoveStrings and EncodeStrings enabled.

All five matched. Two object keys built from identical literals still counted as two entries and still returned their own values. Insertion order still came back as inserted rather than sorted. A deleted and re-added key still moved to the end. The Set still held four members from six inputs, and still reported that it contained NaN. The structuredClone still produced an independent copy whose nested Map was still a Map and still held its value.

Why it holds: a collection key is a value, not a name

Renaming operates on the text of your program. It rewrites identifiers, and it rewrites property names in three specific places: an identifier key in an object literal, a string key in an object literal, and a dotted member access. Everything it touches is a name that appears in the source.

A collection key is not a name in the source. When you write map.set("total", 1), the word total is an argument value, in the same category as the number 1. It is data being handed to a method at run time, and the rename transforms have no reason to treat it as a name, because syntactically it is not one.

This is the same distinction that makes obj["total"] behave differently from obj.total, taken one step further. With a collection there is no dotted form at all, so there is no second spelling that could diverge from the first.

In a string-table build the literal moves into the table with every other string, which changes how the file reads without changing what the key is at run time. That is why the string-table column matched too.

A practical use: keys you need to survive

That structural fact is a design tool, not just an explanation. If you have an object whose key names must match something outside your program, a wire format, a stored blob, a message envelope, then a collection is a way to put those keys beyond the reach of a member pattern entirely.

The alternative approaches are all fine and all require ongoing discipline: keep member renaming off, scope the pattern so it cannot match those names, or maintain an exclusion list. Each is a rule someone has to remember when the code changes. Holding the data in a Map instead makes the keys values, which needs no rule at all.

Measured on the same sample, with member renaming switched on and a pattern matching the object properties the code itself declared, the collection behaviour was still identical on both targets. The rename applied to the plain-object properties it was asked to rename, and the collection contents were unaffected, which is exactly the split described above.

The reverse of that measurement is worth stating too. A pattern that matches the names of built-in collection methods, such as has, add or size, will rewrite those call sites and break the collection, because a member pattern applies wherever the name appears and does not check who owns the method. Keep platform vocabulary out of the pattern.

What a collection still does not give you

Two expectations are worth correcting, because both survive protection unchanged and neither is what people sometimes hope.

A WeakMap is not private storage. It is an excellent way to attach state to an object without adding a visible property and without preventing collection, and it makes the association invisible to code that merely enumerates properties. It does not hide anything from a reader with a debugger, who can pause where the value is used and inspect it, on a protected build exactly as on an unprotected one.

Nor does the object-identity rule provide secrecy. Two objects with identical contents being distinct keys is a semantics guarantee, useful for caching and for tracking instances; it is not a barrier to anyone reading the running program.

The rule is the same one that holds across the client: this is organisation, not concealment. Anything that must stay unknown to the person running the browser belongs behind an API call rather than in a collection, however that collection is keyed.

Scope, and how to check your own build

This article covers storage, lookup, identity, ordering and cloning for Map, Set, WeakMap and WeakSet, measured on the default configuration and the four variants named above, with order observed through forEach and Array.from. It does not cover for...of iteration over collections, which is a question about how a value is consumed rather than how it is stored, and belongs with the iteration protocol rather than here.

One further contrast is worth knowing if you are choosing between a plain object and a Map: the two do not order their keys by the same rule. A plain object puts integer-like keys first in ascending numeric order before the rest; a Map preserves insertion order for every key, including "1". That difference exists before protection and is unchanged by it, and the key-order article covers it directly.

To check your own build, print the contents of your collections at a few checkpoints, protect the file with your project's configuration, run both under Node, and diff. Expect no difference. If one appears, look first at whether a member pattern is matching collection method names.

Frequently asked questions

Does member renaming rewrite the keys stored in a Map?

No. A Map key is an argument value at run time, not a name in the source, so it falls outside all three places renaming rewrites a property name. Measured across five configurations, string keys, object keys and NaN keys all behaved identically after protection.

Is Map insertion order preserved after protection?

Yes. Measured through forEach and Array.from, entries came back in insertion order, and a deleted and re-added key still moved to the end. Note that a Map orders differently from a plain object: an object puts integer-like keys first, while a Map keeps insertion order for every key.

Do two identical-looking objects stay distinct Map keys?

Yes. Object keys compare by identity, not contents, and that is unchanged by protection. Measured, two objects built from identical literals remained two separate entries and each returned its own value.

Does structuredClone still work on a protected build?

Yes. Measured, a clone of an object containing a nested array, a Map and a Set produced an independent copy, the nested Map was still a Map and still held its value, and mutating the clone did not affect the original.

Can I use a Map to keep key names safe from renaming?

Yes, and it is a reasonable design choice when key names must match a wire format or stored data. Because collection keys are values rather than names, they are outside the rename sites by construction, with no exclusion list or pattern discipline to maintain as the code changes.

Is a WeakMap a private place to store data in a protected build?

No. A WeakMap keeps state off the object's visible properties and allows collection, which is what it is for. It does not conceal a value from someone reading the running program with a debugger. Anything that must stay unknown belongs behind an API call.

Related reading