Protect Object Declaration removes the visible shape of object literals. Instead of emitting { key: value }, the output calls a generated helper with the keys and values as a flat argument list, so the association between a key and its value is no longer readable from the source layout.
Example
Without this option:
var obj = { name: a[11], value: a[25] };
With this option enabled:
var obj = b(a[32], a[11], a[33], a[25]);
The braces are gone and so is the structure. What remains is a call with four arguments; knowing that arguments alternate key, value, key, value is something a reader has to work out from the helper, and the helper is itself subject to renaming and the other transforms you have enabled.
The generated helper
The engine inserts a small builder into the wrapper scope. In readable form it is:
function _new$obj_() {
var o = {};
for (var i = 0; i < arguments.length; i += 2)
o[arguments[i]] = arguments[i + 1];
return o;
}
Two consequences follow directly from that shape, and both matter:
- Insertion order is preserved. The loop assigns pairs left to right, so the resulting object enumerates its keys in the same order the literal did. Code that depends on
Object.keys ordering or on JSON.stringify field order keeps working.
- The result is a plain object. The helper starts from
{} and does ordinary assignment, so the product is an ordinary data object — nothing exotic in the prototype chain, and nothing that behaves differently from what the literal produced.
Configuration
The config alias and the raw engine option are equivalent — set one:
{
"protectObjectDeclaration": true
}
{
"options": {
"ReorderCodeObjectDeclare": true
}
}
This option works inside the generated wrapper that Code Transposition builds, and enabling it causes that wrapper to be created even for a file that would otherwise not need one. That is expected: the helper has to live somewhere your code can reach it.
Why it helps
Object literals expose intent more clearly than almost anything else in a bundle. A configuration object, a feature-flag map, a role-to-permission table, an API request body — each is self-documenting, and each survives name mangling completely intact, because mangling renames identifiers and these are property keys and data.
Even with Move Members and Encode Strings applied, a literal still shows a reader how many fields an object has and which values are grouped together. Rewriting the declaration path removes that grouping. It is a narrow transform aimed squarely at the most information-dense construct in typical application code.
Shapes to verify in your own build
A flat key/value argument list can represent a straightforward literal exactly. Richer literal syntax cannot be expressed that way, so those forms need to be left as written — and object-heavy code is exactly where you should be running tests against protected output rather than trusting inspection:
- Getters and setters (
get x() {}) define accessors, not values.
- Spread (
{ ...base, x: 1 }) copies an unknown set of keys at runtime.
- Computed keys (
{ [k]: v }) resolve their key at runtime.
- Shorthand methods (
{ run() {} }) and __proto__, which has special meaning in a literal but not in an assignment.
Run your suite against the protected bundle, and check anything that reflects over object keys or round-trips objects through JSON. Compatibility validation catches mechanical problems before you execute anything.
Related options
- Move Members hides member access; this option hides object construction. They address opposite ends of the same leak.
- Protect Members renames the keys themselves.
- Code Transposition provides the wrapper scope this option's helper lives in.
- Migrating from the open-source
javascript-obfuscator package? Its transformObjectKeys option covers similar ground and is accepted for migration review — see npm migration.
Why it helps: object literals often expose intent very clearly. Rewriting the declaration path makes those structures less obvious during static inspection — and unlike identifier renaming, it targets information that renaming cannot touch.
Frequently asked questions
What does this option actually change in the output?
It removes the visible shape of object literals. Instead of emitting an object with braces and key-value pairs laid out in source order, the output calls a generated builder with the keys and values flattened into a single argument list. The braces are gone, and so is the visual association between a key and the value next to it. What a reader sees is a call with a run of arguments, and working out that they alternate key and value means reading the helper first.
How does the generated helper work?
The engine inserts a small builder into the wrapper scope. It creates an empty object and walks its arguments two at a time, assigning each pair as a property name and value, then returns the object. That shape is worth knowing because two consequences follow from it directly: the arguments are evaluated in the order you wrote them before the object exists, and the helper itself is an ordinary function in your output, so it is subject to renaming and to whatever other transforms you have enabled.
Does it change the behaviour of my objects?
The resulting object has the same keys and the same values, so code that reads it sees what it always did. The difference worth checking is that construction now happens through a function call rather than through a literal, which matters in a few narrow places: anything that depends on the exact evaluation order of a complex expression inside the literal, and anything that inspects the object at the moment of creation. Ordinary data objects are unaffected. Verify the unusual shapes in your own build rather than assuming.
Which object shapes should I verify before shipping?
The ones where a literal is doing more than carrying data. Objects containing getters or setters, computed keys, spread elements, shorthand methods, and any literal whose values are expressions with side effects worth ordering. Also anything that is immediately serialised or compared by key order. These are the shapes where a flattened builder call and a literal are least alike, so they are where a quick test earns its place.
Does it help if the keys are still readable in the argument list?
It helps less on its own than it does in combination, which is the honest framing. The keys are still present as strings in the call, so somebody reading closely can recover them. What is gone is the structure that made them scannable at a glance. Pair it with the option that moves strings into a table and those keys stop being adjacent text as well, at which point recovering the object means following indices into the table rather than reading a literal.
Which plan tier does it need, and what pairs with it?
It is an Enterprise-tier option. It pairs naturally with the string table and with member access rewriting, because all three attack the same thing from different angles: the readable association between a name and the code that uses it. Enabling it alone gives you the smallest part of that benefit, so treat it as one member of a group rather than as a standalone setting.