These questions ask you to predict property access, distinguish direct ownership from inheritance, and identify what object inspection or copying produces. A short classification of the operation is more reliable than guessing from the property value.
Classify the property operation
First identify how the property name is formed. Dot access uses the identifier written after the dot: profile.city always asks for the property named city. Bracket access evaluates its expression first: if field contains city, profile[field] asks for that same property. Numbers used as ordinary property names are converted to strings, while Symbols remain Symbols.
| Goal | Rule to apply |
|---|---|
| Read or write a property | A read of an absent property produces undefined. Under normal writable conditions, assigning through an absent name creates a directly owned property; for example, profile.city = Lima stores that value on profile. |
| Test presence or ownership | The in operator searches the object and its prototype chain. Object.hasOwn checks only the supplied object itself. |
| Enumerate ordinary visible data | Object.keys returns directly owned enumerable string names. Object.values returns their values, and Object.entries returns corresponding name-and-value pairs. Inherited and Symbol-named properties are omitted. |
| Copy with object spread | Spread copies enumerable directly owned properties one level deep. If a copied value is another object, both outer objects still refer to that same nested object; a later occurrence of the same name replaces an earlier one. |
| Access through a possibly nullish value | Optional chaining stops at its optional link when the left side is null or undefined and produces undefined. For example, missingUser?.city does not attempt the city access. |
For deletion, keep the target separate from local variables. Deleting a configurable directly owned property removes that property and evaluates to a boolean status. It does not erase a lexical binding that happens to have the same spelling.
Where the mistakes come from
| Tempting route | How to reject it |
|---|---|
| Treating a number as a third property-name type, or treating dot access as a variable lookup | Ordinary names end as strings or Symbols. A dot fixes the written identifier; only brackets evaluate an expression that can change which property is selected. |
| Turning an absent read into an exception, or assuming an absent property cannot be assigned | A normal read completes with undefined, while an ordinary assignment can create the property. Errors from accessing through null or undefined are a different case. |
| Using one presence test for both inherited and directly owned properties | Decide whether the prototype chain should count. Use in when it should, and Object.hasOwn when only the object itself should count. |
| Assuming enumeration includes inherited or Symbol-named data, or assuming spread recursively clones nested objects | The common Object enumeration methods use directly owned enumerable string names, and spread is shallow. Check ownership, enumerability, name type, and depth separately. |
Try it
Which statements about bracket property access are true? Select all that apply.
- If key holds the string name, object[key] accesses the name property
- object.key always accesses the same property as object[key]
- Changing the value of key can make object[key] select another property
- The expression inside brackets must be a string literal
base.rank is 1, and item inherits from base without defining rank itself. What does Object.hasOwn(item, "rank") return?
- 1, because it returns the inherited value
- undefined, because it returns the missing own value
- true, because inherited properties count as owned
- false, because rank is not item's own property
The variable value is null. What does the expression value?.name produce?
- The string name
- The value null
- A TypeError
- The value undefined