AlgoInsight

Free lesson · JavaScript Basic to Advance

Property Access, Mutation, and Deletion

Easy5 min read

Working With Object Properties

Once an object exists, you need to read its properties, change them, add new ones, and sometimes remove them. JavaScript gives you two syntaxes for accessing properties and a few approaches for each kind of modification.

const user = {
  name: "Olena",
  age: 35,
  role: "Head of Product"
};

// Reading
console.log(user.name);
console.log(user["age"]);

// Writing — modifying existing
user.age = 36;

// Writing — adding new
user.location = "Redmond";

// Deleting
delete user.role;

console.log(user);

These four operations — read, modify, add, delete — are the basic vocabulary for working with objects. This Topic covers each with the subtleties that catch people off guard.

Dot Notation vs Bracket Notation

There are two ways to access a property: dot notation (obj.key) and bracket notation (obj["key"]). They do the same thing, but each has cases where it's the only option.

Dot notation works when the key is a valid JavaScript identifier — letters, digits, underscores, dollar signs, doesn't start with a digit, isn't a reserved word.

const user = { name: "Olena", age: 35 };

console.log(user.name);
console.log(user.age);
// user.first-name   // ❌ would be parsed as user.first - name
// user.2023         // ❌ syntax error, can't start with digit

Bracket notation works for any string key, including those that aren't valid identifiers:

const data = {
  "first-name": "Olena",
  "2023-revenue": 1000000,
  "user id": 42
};

console.log(data["first-name"]);
console.log(data["2023-revenue"]);
console.log(data["user id"]);

Bracket notation also lets you use a variable as the key — something dot notation can't do:

const user = { name: "Olena", age: 35, role: "Head of Product" };
const keyName = "name";

console.log(user[keyName]);            // "Olena"
console.log(user.keyName);             // undefined — looks for property literally named "keyName"

This is the most common reason to use bracket notation: when you need to access a property whose name is determined at runtime. Looping over keys, accessing properties from configuration, looking up values by computed name — all of these use brackets.

graph LR
    A[Property Access] --> B[Dot notation:<br/>obj.key]
    A --> C[Bracket notation:<br/>obj key in brackets]

    B --> D[Key is hardcoded identifier]
    B --> E[Cannot use variable as key]

    C --> F[Key can be any string]
    C --> G[Can use variables/expressions]

    style B fill:#7c3aed,color:#fff
    style C fill:#10b981,color:#fff
This diagram contrasts dot notation and bracket notation for property access. Dot notation works only when the key is a hardcoded identifier that's a valid JavaScript name. You cannot use a variable as the key with dot notation. Bracket notation works for any string key, including names with special characters, and crucially allows you to use variables or computed expressions to determine the key at runtime.

Accessing Properties That Don't Exist

When you read a property that doesn't exist, JavaScript returns undefined instead of throwing an error:

const user = { name: "Olena" };

console.log(user.name);    // "Olena"
console.log(user.age);     // undefined
console.log(user.color);   // undefined

This is convenient but can be a source of subtle bugs. Code that expects a value gets undefined and silently misbehaves. The fix is to check before using:

const user = { name: "Olena" };

if (user.location) {
  console.log(`Lives in ${user.location}`);
} else {
  console.log("Location not specified");
}

Modern JavaScript has optional chaining (?.) and nullish coalescing (??) for cleaner versions of these checks — we cover both in Module 10.

What's more dangerous is reading properties on undefined or null directly:

const user = { name: "Olena" };

console.log(user.address);   // undefined — fine
// console.log(user.address.city);   // ❌ TypeError: Cannot read property 'city' of undefined

Trying to access .city on undefined throws. The first level returns undefined quietly; chaining through it throws loudly. We'll return to safe access patterns in Module 10.

Interactive animation: Missing is safe, missing-dot-more is fatal - reading through undefined throws a TypeError.

Adding and Modifying Properties

Properties can be added to or modified on an object at any time:

const user = { name: "Olena" };

// Add new property
user.age = 35;
user["role"] = "Head of Product";

// Modify existing
user.name = "Olena Marchenko";

console.log(user);

There's no separate "add" vs "modify" syntax — assignment handles both. If the property doesn't exist, JavaScript creates it. If it exists, JavaScript overwrites it.

This works even on objects declared with const:

const user = { name: "Olena" };

user.age = 35;          // ✅ allowed — modifying the object
user.name = "Nikhil";   // ✅ allowed — modifying a property
console.log(user);

// user = { name: "Someone Else" };   // ❌ would throw — reassigning the variable

We covered this in Topic 3.1: const prevents reassignment of the variable, not modification of the value it holds. To actually prevent modifications, you'd use Object.freeze — covered in Topic 7.7.

Interactive animation: The binding is frozen, the object is not - const allows changes inside but blocks reassignment.

Deleting Properties

The delete operator removes a property from an object entirely:

const user = { name: "Olena", age: 35, role: "Head of Product" };

delete user.role;

console.log(user);
console.log(user.role);   // undefined — property no longer exists

After delete, the property is gone. It's not set to undefined — it doesn't exist at all. You can verify this with the in operator:

const user = { name: "Olena", age: 35 };

console.log("name" in user);    // true
console.log("age" in user);     // true
console.log("color" in user);   // false

delete user.age;
console.log("age" in user);     // false — property no longer there

delete is one of those operators you rarely use in modern code. More commonly, you create a new object without the property using spread syntax:

const user = { name: "Olena", age: 35, role: "Head of Product" };

// Instead of mutating with delete, create a new object
const { role, ...rest } = user;
const userWithoutRole = rest;

console.log(user);              // original is unchanged
console.log(userWithoutRole);   // new object without role

This pattern uses destructuring and the rest operator (covered in Module 5 and again in Module 10). It avoids mutation, which is preferred in functional-style code. Use delete when mutation is acceptable; use the non-mutating pattern when it isn't.

The Property Existence Check

There are three ways to check whether an object has a property, each with subtleties:

const user = {
  name: "Olena",
  age: 0,
  role: undefined
};

// 1. Property access — returns undefined if missing
console.log(user.name);        // "Olena"
console.log(user.role);        // undefined
console.log(user.missing);     // undefined
// Problem: can't distinguish "exists but is undefined" from "doesn't exist"

// 2. The 'in' operator — checks key existence
console.log("name" in user);     // true
console.log("role" in user);     // true — exists, even though value is undefined
console.log("missing" in user);  // false
// Best for: distinguishing "set to undefined" vs "not set"

// 3. hasOwnProperty (older) / Object.hasOwn (modern)
console.log(Object.hasOwn(user, "name"));     // true
console.log(Object.hasOwn(user, "missing"));  // false
// Best for: checking own properties (ignoring inherited ones)
// We'll see why this matters when we cover prototypes in Topic 7.5

For most code, the difference doesn't matter and in is fine. The distinction becomes important when working with prototype inheritance, which we cover later in this module.

Interactive animation: Setting undefined keeps the key, delete removes it - and the in operator tells the two apart.

Common Pitfalls

Interactive animation: ?. stops instead of crashing - optional chaining returns undefined where a plain chain would throw.

Warning: The property access traps.

Chaining through possibly-missing properties. user.address.city throws if address is undefined. Always check intermediate values, or use optional chaining (user?.address?.city).

Confusing "missing" with "undefined". A property set to undefined exists. A missing property also returns undefined when accessed. To distinguish them, use in or Object.hasOwn.

Using delete in performance-sensitive code. delete can prevent certain engine optimizations. In hot paths processing many objects, prefer creating new objects without the property instead.

Why This Matters in Interviews

Interview: Property access patterns appear constantly in coding problems. Interviewers watch whether you reach for dot notation when keys are static and bracket notation when they're dynamic. The wrong choice — using brackets with hardcoded strings, or dot notation with variables — signals unfamiliarity with the language.

The more conceptual question that comes up: "What's the difference between accessing a property that's set to undefined and one that doesn't exist?" The answer involves the in operator and Object.hasOwn.

Topic 7.8 — Interview Lens: Objects and Prototypes — covers the deeper interview treatment.

Try It

Exercise: Build a small "settings" object and exercise all four operations on it:

  1. Create an object with at least three properties
  2. Read each property using a mix of dot and bracket notation
  3. Read a property that doesn't exist — observe undefined
  4. Modify one of the properties
  5. Add two new properties — one using a literal key, one using a variable as the key
  6. Use in to check whether a specific property exists
  7. Delete one of the properties
  8. Verify with in that the deleted property is gone
const settings = {
  // your object here
};

// your operations here

The point of this exercise is muscle memory. By the time you finish, the syntax for each operation should be automatic.

Back to JavaScript Basic to Advance