Command Palette

Search for a command to run...

Hectal
PHASE 6Beginner ~25 min· topic 2 of 7

Topic 6.2

Reading and Changing Properties

In one line

You read and write properties with a dot (obj.name) or square brackets (obj[key]), add them by assigning, remove them with delete, test for them with in or Object.hasOwn, and read deep values safely with optional chaining ?..

Think of it like this

A row of school lockers with name tags. To open Asha's locker you can walk to the tag that says "Asha" (that's the dot: you know the name while writing the code), or a teacher can hand you a slip of paper with a name on it and you go to whichever locker the slip says (that's square brackets: the name is only known while the program runs). Asking for a locker that doesn't exist gives you nothing (undefined). Asking for a locker in a building that doesn't exist is a real problem (an error).

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Dot notation
Reading or writing a property by writing its name after a dot: user.name.
Bracket notation
Reading or writing a property with an expression in square brackets: user["name"] or user[key].
Own property
A property stored on the object itself, not borrowed from its prototype.
Prototype
A parent object that an object looks in when it doesn't have a property itself (Phase 7.3).
Inherited property
A property an object can use because its prototype has it, like toString.
delete
An operator that removes a property from an object completely.
Optional chaining
?.: read a property or call a method only if the value on the left isn't null or undefined.
TypeError
The error you get when you use a value the wrong way, like reading a property of undefined.

Step by step

01Read with a dot

When you know the key while writing the code and it's a valid name, use a dot. It's the shortest and easiest to read.

app.jswhole filejavascript
const book = { title: "Malgudi Days", pages: 256 };
console.log(book.title); // Malgudi Days

02Read with brackets when the key varies

Here the key comes from a variable. book.field would look for a property literally called field; book[field] uses the variable's value.

app.jswhole filejavascript
const field = "pages";
console.log(book[field]); // 256
console.log(book.field);  // undefined

03Add, change and delete

Assigning to a key that doesn't exist creates it. Assigning to one that exists replaces its value. delete removes it.

app.jswhole filejavascript
book.author = "R. K. Narayan"; // added
book.pages = 260;              // changed
delete book.pages;             // removed
console.log(book); // { title: 'Malgudi Days', author: 'R. K. Narayan' }

04Check whether a key exists

Comparing with undefined can't tell "missing" from "present but undefined". Use Object.hasOwn for the object's own keys and in when inherited ones count too.

app.jswhole filejavascript
console.log(Object.hasOwn(book, "title")); // true
console.log("toString" in book);           // true (inherited)
console.log(Object.hasOwn(book, "toString")); // false

05Follow a chain safely

Each ?. checks the value on its left. The first one that is null or undefined stops the chain and the result is undefined. Nothing to the right of it runs.

Follow a chain safelydiagram
Rendering diagram…

Try it yourself

  1. 1

    Read a key from a variable

    In the first example, change key to "page count". Predict what book[key] prints now, then run.

  2. 2

    Undefined versus missing

    In the second example, add console.log(Object.keys(cart)); before and after the delete. Predict both lists.

  3. 3

    Give Ravi an address

    In the third example, add address: { city: "Kochi" } to Ravi. Predict every line of the console, including the last one.

Code & diagrams

Dot vs brackets New tab

`book.key` looks for a property named `key`, which doesn't exist. `book.author` doesn't exist either: missing properties read as `undefined`, without an error.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Wings of Fire
180
1999
undefined
undefined
Add, change, delete and check ES2022+ New tab

Reading `cart.cherries` gives `undefined` both before and after `delete`; only `in` (or `Object.hasOwn`) can tell that the key was there. `Object.hasOwn` is ES2022; the rest is much older.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

{ apples: 5, bananas: 6, cherries: undefined }
true undefined
false undefined
true false
true
Optional chaining in a list of users ES2020+ New tab

`u.greet?.()` calls the method only if it exists. The last block shows what happens without `?.`: Ravi has no `address`, so reading `.city` of `undefined` throws.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Asha Pune Pune Hi, I'm Asha
Ravi undefined city unknown undefined
undefined
TypeError: Cannot read properties of undefined (reading 'city')
Every key becomes a string New tab

Both objects turned into the same string key, `"[object Object]"`, so the second write replaced the first. A `Map` (Phase 14.3) can use objects as real keys.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

number one
for b
[ '1', '[object Object]' ]

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Read deep into something missing

In the third example, replace u.address?.city with u.address.city in the line that sets city.

terminal
$ Console
── what you'll see ──
Uncaught TypeError: Cannot read properties of undefined (reading 'city')

Break #2

Dot with a variable

In the first example, write book.key where you meant the value of key.

terminal
$ Console
── what you'll see ──
undefined

Myth vs fact

Myth

If obj.x === undefined, the object has no x property.

Fact

The property may exist and hold undefined. Use Object.hasOwn(obj, "x") or "x" in obj to check existence.

Myth

delete frees memory immediately.

Fact

delete only removes a property from one object. Memory is freed later by the garbage collector, and only if nothing else refers to the value.

Myth

Optional chaining should go everywhere to prevent errors.

Fact

It hides bugs when a value should always be there. Use it where missing data is a normal case (optional fields, API data), not to silence real mistakes.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    delete can push a V8 object into dictionary mode (a slower hash-table storage), because the object no longer matches a shared shape. In hot code, prefer building a new object without the key (rest destructuring, Topic 6.3) or setting it to undefined if the key may stay.

  • ▸

    ?. short-circuits the whole chain: in a?.b.c.d, if a is nullish none of .b.c.d runs. Parentheses end the chain: (a?.b).c throws if a is nullish. You can't assign through it: a?.b = 1 is a SyntaxError.

  • ▸

    in also works for arrays (2 in ["a", "b", "c"] is true, checking indexes) and is the standard way to feature-detect: "IntersectionObserver" in window.

  • ▸

    In strict mode (modules and classes), delete on a non-configurable property throws a TypeError instead of returning false, and delete someVariable is a SyntaxError.

Remember this

  1. 1

    Dot notation, obj.key, is the everyday way. The key must be a valid name, written directly in the code. Bracket notation, obj[expression], works out the expression first and uses the result, turned into a string, as the key. You need brackets when the key is in a variable (obj[field]), has spaces or dashes (obj["first-name"]), starts with a digit, or is built from pieces (obj["item" + i]). obj.field and obj[field] are different: the first looks for a key literally named field.

  2. 2

    Reading a property that doesn't exist is not an error: you get undefined. That's handy and dangerous at once: a typo like user.nmae gives undefined silently. But reading a property of undefined or null throws: undefined.city gives TypeError: Cannot read properties of undefined (reading 'city'). This is the most common error in JavaScript.

  3. 3

    Assignment both changes and adds: obj.age = 15 changes age if it exists and creates it if it doesn't. **delete obj.key** removes the property completely and returns true. Setting a property to undefined is different: the key still exists, it only holds undefined. Use delete when the key must really disappear (for example, before sending data or listing keys).

  4. 4

    To ask "does this property exist?", there are two tools. The **in operator**, "key" in obj, is true if the object has the key or inherits it from its prototype (a parent object it looks things up in; every plain object inherits methods such as toString from Object.prototype, explained in Phase 7.3). **Object.hasOwn(obj, "key")** (ES2022) is true only for the object's own properties. The older form is obj.hasOwnProperty("key"); Object.hasOwn is preferred because it also works on objects that have no prototype or that override hasOwnProperty.

  5. 5

    Optional chaining ?. (ES2020) reads a property only if the thing on its left is not null or undefined; otherwise the whole expression stops and gives undefined instead of throwing. It has three forms: user?.address (property), user?.[key] (brackets) and user.greet?.() (call the method only if it exists). Pair it with ?? (Phase 2) to give a fallback: user.address?.city ?? "unknown".

  6. 6

    Keys are always converted to strings (unless they are symbols). obj[1] and obj["1"] are the same property, and obj[true] uses the key "true". Using an object as a key is a classic bug: it becomes the string "[object Object]", so every object key collides. When keys must be objects, use a Map (Phase 14.3).

Explain it without notes

01

When must you use bracket notation instead of dot notation?

02

What's the difference between in, Object.hasOwn and comparing with undefined?

03

What does optional chaining do, and where is it the wrong tool?

04

Why does obj[{}] usually cause bugs?

Practice

01

Write getField(obj, field) that returns the value of any field name passed in, or the text "missing" if the object doesn't have that own property. Test it with { a: 1, b: undefined } and the fields a, b and c.

02

Given const order = { customer: { name: "Asha" } }, log the customer's phone number, or "no phone", in one expression without errors. Then do the same for const order2 = {}.

03

Start with const settings = { theme: "dark", debug: true }. Remove debug, add fontSize: 16, and log the result.

Trade-offs

  • ↔

    Dot notation is clearer and lets editors catch typos and autocomplete; bracket notation is flexible but hides which keys are used, so prefer dots unless the key really varies.

  • ↔

    Optional chaining makes code that handles missing data short and safe, at the cost of hiding mistakes; validating data once at the edge (when it arrives) often beats sprinkling ?. everywhere.

Done when you can

  • I can choose between dot and bracket notation and explain why.

  • I can add, change and delete properties.

  • I can check for a property with in and Object.hasOwn and explain the difference.

  • I can read nested values safely with ?. and give a fallback with ??.

  • I know that keys become strings and why object keys collide.