- Which implementation is the most appropriate default choice?
Answer: Use a Map, because its entries are separate from prototype properties and all supplied strings are ordinary keys.
When you need a key-value store where the keys are arbitrary strings — especially strings like toString, constructor, or __proto__ — the critical question is whether those keys might collide with built-in properties. Plain objects inherit from Object.prototype, meaning properties like toString already exist on every object before you add a single entry. That shadow can cause silent bugs when checking for existence, iterating, or calling methods.
A Map sidesteps this entirely. Its entries live in an internal slot completely separate from any prototype chain, so every string you supply — no matter how dangerous-sounding — is treated as an ordinary key with no risk of collision. Since the application also frequently adds, checks, and removes entries, Map is ideal: it provides set, has, and delete as first-class methods designed exactly for this workflow. That makes B the correct choice.
A is tempting because bracket notation does create own properties, but it doesn't protect you from inherited properties on Object.prototype. Calling obj.hasOwnProperty can itself be shadowed if someone passes "hasOwnProperty" as a name. C makes things worse — the in operator explicitly searches the prototype chain, so "toString" in obj returns true even when you never added it, giving false positives. D is counterproductive; filtering out names that exist on Object.prototype defeats the entire purpose of the feature, since the application is specifically required to handle those names.
As a study tip: whenever a question involves keys that might collide with Object.prototype properties, reach for Map first — it's the safest and most semantically correct default.
- Which implementation most directly expresses both requirements?
Answer: Use a Map and use has(key) for presence, then get(key) to retrieve the stored result.
When designing a cache that must store undefined as a legitimate value, you need to think carefully about two separate concerns: how keys are stored and how presence is checked. These are distinct problems, and the best solution addresses both explicitly.
A Map is purpose-built for arbitrary string keys because it has no prototype chain — unlike plain objects, there's no risk of a key like "constructor" or "toString" colliding with an inherited property. This eliminates the inherited-property collision problem entirely. Then, to distinguish a missing key from a key whose stored value is undefined, you call has(key) first — which returns a boolean about existence — and only then call get(key) to retrieve the value. That's why D is correct: it directly solves both requirements with the right tool for each job.
A fails on both counts. A plain object risks prototype collisions, and using cache[key] === undefined can't distinguish "key was never set" from "key was set to undefined" — they both return undefined.
B improves on A by using key in cache, which does correctly detect presence even for undefined values. However, it still uses a plain object, leaving you vulnerable to inherited-property collisions on keys like "hasOwnProperty".
C moves to Map, solving the collision problem, but then discards that advantage by checking get(key) !== undefined — which again conflates a missing key with a stored undefined value, the exact bug you're trying to avoid.
As a study tip: when you see a cache or key-value store that needs to hold any value including undefined, immediately think Map + has() — never rely on value comparison alone to infer absence.
- Which collection satisfies all three key rules without manually encoding key types?
Answer: Use a Map, because its key comparison groups NaN, distinguishes 1 from "1", and treats signed zeros alike.
When choosing a collection for keying data by primitive values, you need to understand how each structure compares keys internally. JavaScript's Map uses the SameValueZero algorithm, while plain Object coerces all keys to strings.
Map's SameValueZero behaves like strict equality (===) with one important exception: NaN is considered equal to itself. This means repeated NaN keys map to the same entry — satisfying the first requirement. Because Map preserves the original type of each key, numeric 1 and string "1" are stored as genuinely different keys — satisfying the second requirement. Finally, SameValueZero treats 0 and -0 as equal, so they share the same group — satisfying the third requirement. B is correct because it accurately describes all three behaviors.
A is wrong because Object coerces all keys to strings, meaning numeric 1 and string "1" both become the string "1" — a single property, not two distinct ones. The claim that coercion "keeps them distinct" is the opposite of reality.
C is wrong for the same reason: Object does not produce three distinct key types for NaN, 1, and "1". NaN becomes the string "NaN", and 1 becomes "1" — colliding with the string "1" key entirely.
D is subtly wrong because it claims Map distinguishes NaN from itself, which is false — Map intentionally groups repeated NaN keys together. It also incorrectly implies 0 and -0 are kept separate.
A useful rule of thumb: whenever a question involves mixed-type keys or special values like NaN and -0, reach for Map first and verify which equality algorithm applies.
- Which representation most directly supports these requirements?
Answer: Use an Object, because property descriptors can define accessors and control whether individual properties are enumerable.
When a question asks you to match a data structure to a specific set of requirements — accessors, enumeration control, and Object.keys compatibility — think about property descriptors. These are the mechanism that gives JavaScript objects fine-grained control over individual properties.
Plain JavaScript objects support property descriptors through Object.defineProperty, which lets you declare a property as a getter/setter (accessor), mark it non-enumerable, or both — all independently per property. This means you can have some fields show up in Object.keys (enumerable), one summary property implemented as a getter, and one internal property hidden from enumeration — exactly what the scenario demands. That's why B is correct: objects with property descriptors directly satisfy every stated requirement.
A is wrong because Map entries have no concept of property descriptors at all. Maps don't support accessor properties or enumerability flags — those are object-specific features. You simply cannot define a getter on a Map entry.
C is wrong for a similar reason: Object.keys doesn't work on Maps the way the answer implies. Object.keys on a Map object returns keys of the Map object itself (likely empty), not its entries. To iterate Map entries you'd use .forEach or for...of — and still couldn't control enumerability.
D is a factual inversion. Properties added via simple assignment (obj.x = 1) start as enumerable by default, not non-enumerable. Non-enumerability requires Object.defineProperty with enumerable: false.
As a study tip: whenever you see accessor properties or non-enumerable properties mentioned, your mental trigger should be Object.defineProperty — that's the only path to those features in JavaScript.
- Which representation most directly implements the intended fallback behavior?
Answer: Use an Object created with the default messages object as its prototype, then add locale-specific own properties.
When you see a question about prototype-based inheritance in JavaScript, think about how property lookup naturally "falls through" to a parent object when a property isn't found on the child. This chain is JavaScript's built-in fallback mechanism — and it's exactly what this localization scenario needs.
The most direct implementation is D: creating a locale object using Object.create(defaults), where defaults is your default messages object. When you access a property on the locale object, JavaScript first checks its own properties (locale-specific overrides). If nothing is found, it automatically walks up the prototype chain to defaults and returns that value — zero extra code required. This is precisely the "automatic retrieval through prototype lookup" the question describes.
A is tempting but fundamentally wrong: Map objects don't use prototype chains for their entries. Setting one Map as the prototype of another only affects the Map object itself, not the key-value pairs stored inside it. Calling .get() on a locale map would never traverse into the default map's entries.
B describes behavior that simply doesn't exist. Map lookup does not automatically continue into another map when a key is absent — this is a fabricated feature. A missing key returns undefined, full stop.
C describes a manual copy strategy (like Object.assign). While functional, it duplicates all default data into every locale object upfront. This is wasteful, doesn't use prototype inheritance, and fails to match the "automatic retrieval through prototype lookup" requirement stated in the passage.
A useful rule of thumb: whenever a question mentions "automatic fallback" or "prototype lookup," your answer should involve Object.create() or direct __proto__ assignment — not Map, and not copying properties.
- Which design most directly preserves the required iteration behavior?
Answer: Use a Map and iterate over its keys, because map iteration follows entry insertion order.
When you need to preserve insertion order for keys that may look like integers, the data structure you choose matters enormously — and this is exactly what this question tests.
Map is purpose-built to guarantee iteration in insertion order for all key types, including strings like "10", "2", and "1". When you iterate a Map using map.keys(), map.values(), or for...of, the order always reflects the sequence in which entries were added — no exceptions. That makes B the correct, direct solution to the problem.
Here's where the distractors get tricky. A claims that Object.keys always respects insertion order for string keys, but this is false for integer-like string keys (e.g., "1", "2", "10"). JavaScript engines sort these numerically and hoist them before other string keys, regardless of when they were inserted. C makes the same factual error about for...in — integer-like keys are still reordered numerically with for...in, making it just as unreliable as Object.keys for this use case. The ECMAScript spec explicitly mandates this numeric-first ordering for objects. D is self-defeating: it suggests using a Map but then sorting its keys before iterating, which actively destroys the insertion order you're trying to preserve.
A useful rule of thumb: whenever your keys could be integer-like strings and insertion order is critical, reach for Map — not Object. Objects have a hidden sorting rule for numeric keys that silently breaks your assumptions.
- Which representation is most suitable for the request body?
Answer: Use an Object, because its named enumerable own properties are directly represented in the resulting JSON object.
When you call JSON.stringify on a value, JavaScript follows a specific set of rules to determine what ends up in the output string. Understanding those rules is the key to this question.
Plain objects serialize naturally because JSON.stringify iterates over an object's own enumerable properties and maps each key-value pair directly into a JSON object property. So { name: "Ada", email: "ada@example.com", active: true } becomes exactly the JSON your server expects — no extra steps, no configuration needed. That makes C the correct choice: an object's named enumerable own properties are precisely what JSON.stringify is designed to represent.
A and B both fall into the same trap: assuming Map works with JSON.stringify. It does not. A Map is not a plain object, and JSON.stringify has no built-in logic to serialize its entries. Calling JSON.stringify on a Map produces "{}" — an empty object — because Map instances have no own enumerable properties that hold the data. The key-value pairs live in an internal slot, invisible to the serializer. A is factually wrong about the behavior, and B invents a "stronger serialization guarantee" that simply doesn't exist.
D is technically valid but unnecessary. A replacer array can filter or order properties, but when you've already defined a plain object with exactly the fields you need, adding a replacer is redundant boilerplate. It's not the most suitable approach.
As a rule of thumb: if you need JSON.stringify to produce a JSON object, reach for a plain JavaScript object — that's exactly what the method is built around.
- Which storage design best satisfies these requirements without assigning IDs to the elements?
Answer: Use a Map whose keys are the element references and whose values are their state objects.
When working with a question like this, focus on two core requirements: identity-based lookup (using the actual object reference, not a string representation) and uniqueness guarantees (two distinct objects must never collide, even if they look the same when converted to a string).
A Map is uniquely suited here because it uses reference equality for object keys — meaning each DOM element is treated as a unique key based on its place in memory, not its string value. Option A correctly leverages this: you store map.set(element, stateObject) and retrieve with map.get(element), and two visually identical elements will always have separate entries because they're different objects in memory. This is the right answer.
Option B fails because plain JavaScript objects coerce all keys to strings. When you write obj[element] = state, the element becomes "[object HTMLDivElement]" (or similar) — so every element of the same type collides into one key, completely destroying uniqueness.
Option C makes the same mistake explicitly: calling String(element) produces an identical string for different elements of the same type, so two separate elements will silently overwrite each other's state.
Option D uses a Map (good instinct!), but calling JSON.stringify(element) on a DOM element typically returns undefined or "{}" because DOM elements aren't JSON-serializable. Every element would map to the same key — even worse than the string approaches.
Study tip: Whenever a question involves using objects as keys, reach for Map first. Plain objects only accept string (or Symbol) keys, making them unsuitable for reference-based lookups.
- Which choice best matches the collection's semantics?
Answer: Use a Map, because it directly supports dynamic entries, deletion, and a maintained size value.
When choosing between Object and Map in JavaScript, ask yourself three questions: Do I need dynamic key-value pairs? Do I need frequent deletion? Do I need efficient size tracking? If all three are yes, Map is almost always the right tool.
Map is purpose-built for exactly this scheduler scenario. It maintains a size property that updates automatically on every set and delete operation — no manual counting, no iteration required. That makes C correct: Map directly supports dynamic entries, deletion, and a live size value, matching the scheduler's needs precisely.
Now look at the distractors. A is wrong on two levels: using a Proxy to intercept set and deleteProperty on a plain Object just to maintain a count is the opposite of idiomatic — it's an overcomplicated workaround for something Map handles natively. B is wrong because Object.keys(store).length is not constant-time. It must enumerate all enumerable own properties at the moment it's called, making it O(n) — a real performance concern when the scheduler reads size on every cycle. D is a trap for students who confuse Map with arrays: Map has no length property. The correct property is size, and it counts all current entries, not some filtered subset.
The study tip here: memorize that Map.size is a maintained getter (O(1)), while Object.keys().length requires a full traversal (O(n)). On exam questions involving frequent insertions, deletions, and size reads, Map is the signal answer.
- Which representation best fits this public API?
Answer: Use an Object, because it directly supports named fields, destructuring, and object-spread updates.
When choosing between Object and Map for a public API, ask yourself: what syntax do callers actually use? The passage highlights three operations — named property access, destructuring, and object spread — and each one is designed specifically for plain objects.
Answer A is correct because Object natively supports all three patterns. Destructuring syntax (const { id, status } = customer) reads properties from an object, not entries from a Map. Object spread ({ ...customer, status: "closed" }) also operates on object properties, producing a new plain object with shallow-copied own enumerable properties. A plain object is the right tool precisely because its shape maps directly to these language features.
Answer B is wrong because destructuring does not automatically read Map entries. If customer were a Map, const { id, status } = customer would look for properties named id and status on the Map instance itself — finding undefined — not inside its stored entries. You'd need map.get("id") instead.
Answer C is a subtle trap. Object spread (...obj) copies only own enumerable properties — it explicitly excludes inherited properties and prototype methods. So the claim in C is factually false, making it a misleading reason even though it picks Object.
Answer D is wrong because object spread does not automatically unpack a Map's entries into properties. Spreading a Map into an object literal ({ ...myMap }) produces an empty object, since Maps have no own enumerable string-keyed properties.
A useful rule of thumb: if callers will use destructuring or spread syntax, always reach for a plain Object. Reserve Map for dynamic keys, non-string keys, or frequent programmatic get/set operations.