Adding and Deleting Properties in JS Objects
Learn how to add, update, and delete properties on JavaScript objects. Covers dot notation assignment, bracket notation, Object.defineProperty, the delete operator, and immutability patterns with practical examples.
JavaScript objects are mutable by default. You can add new properties, update existing ones, and remove properties at any point after an object is created. This flexibility is powerful, but it also means you need to understand exactly how property mutation works to avoid introducing bugs.
This tutorial covers every way to add, modify, and remove object properties in JavaScript, from simple assignment to Object.defineProperty and immutability patterns.
Adding Properties to Objects
Using Dot Notation
The simplest way to add a property is assigning a value using dot notation:
const user = {
name: "Alice",
age: 28
};
// Add new properties
user.email = "alice@example.com";
user.role = "admin";
user.isActive = true;
console.log(user);
// { name: "Alice", age: 28, email: "alice@example.com", role: "admin", isActive: true }Using Bracket Notation
Bracket notation lets you add properties with dynamic names or names that contain special characters:
const product = {
name: "Laptop"
};
// Add properties with special characters
product["release-date"] = "2025-01-15";
product["price (USD)"] = 999;
// Add properties dynamically
const field = "category";
product[field] = "electronics";
// Add properties with computed names
const version = 2;
product[`version_${version}`] = true;
console.log(product);
// { name: "Laptop", "release-date": "2025-01-15", "price (USD)": 999, category: "electronics", version_2: true }Adding Multiple Properties with Object.assign
Object.assign() copies properties from one or more source objects into a target object:
const user = { name: "Alice" };
Object.assign(user, {
age: 28,
email: "alice@example.com",
role: "developer"
});
console.log(user);
// { name: "Alice", age: 28, email: "alice@example.com", role: "developer" }Adding Multiple Properties with Spread (New Object)
The spread operator creates a new object rather than modifying the original:
const user = { name: "Alice", age: 28 };
const updatedUser = {
...user,
email: "alice@example.com",
role: "developer",
joinedAt: new Date().toISOString()
};
console.log(user); // { name: "Alice", age: 28 } — unchanged
console.log(updatedUser); // { name: "Alice", age: 28, email: "...", role: "...", joinedAt: "..." }Updating Existing Properties
Updating a property uses the same syntax as adding one. If the key already exists, the value is replaced:
const config = {
theme: "light",
fontSize: 14,
language: "en"
};
// Update existing properties
config.theme = "dark";
config.fontSize = 18;
config["language"] = "fr";
console.log(config);
// { theme: "dark", fontSize: 18, language: "fr" }Conditional Updates
const user = {
name: "Alice",
age: 28,
email: "alice@old-email.com",
verified: false
};
// Update only if condition is met
function updateUser(user, updates) {
for (const [key, value] of Object.entries(updates)) {
if (key in user) {
user[key] = value;
}
}
return user;
}
updateUser(user, {
email: "alice@new-email.com",
verified: true,
phone: "555-0100" // Ignored: key doesn't exist
});
console.log(user);
// { name: "Alice", age: 28, email: "alice@new-email.com", verified: true }Object.defineProperty()
Object.defineProperty() adds or modifies a property with fine-grained control over its behavior (writability, enumerability, configurability):
const user = { name: "Alice" };
Object.defineProperty(user, "id", {
value: 42,
writable: false, // Cannot be changed
enumerable: true, // Shows up in Object.keys() and for...in
configurable: false // Cannot be deleted or reconfigured
});
console.log(user.id); // 42
user.id = 99; // Silently fails (writable: false)
console.log(user.id); // Still 42Property Descriptor Options
| Option | Default | Description |
|---|---|---|
value | undefined | The property's value |
writable | false | Can the value be changed with assignment? |
enumerable | false | Does it appear in for...in and Object.keys()? |
configurable | false | Can this property be deleted or its descriptor changed? |
get | undefined | Getter function (accessor descriptor) |
set | undefined | Setter function (accessor descriptor) |
Getter and Setter Properties
const person = {
firstName: "Alice",
lastName: "Johnson"
};
Object.defineProperty(person, "fullName", {
get() {
return `${this.firstName} ${this.lastName}`;
},
set(value) {
const parts = value.split(" ");
this.firstName = parts[0];
this.lastName = parts[1];
},
enumerable: true,
configurable: true
});
console.log(person.fullName); // "Alice Johnson"
person.fullName = "Bob Smith";
console.log(person.firstName); // "Bob"
console.log(person.lastName); // "Smith"Object.defineProperties() for Multiple Properties
const product = {};
Object.defineProperties(product, {
name: {
value: "Laptop",
writable: true,
enumerable: true,
configurable: true
},
sku: {
value: "LAP-001",
writable: false,
enumerable: true,
configurable: false
},
_internalId: {
value: "abc123",
writable: false,
enumerable: false, // Hidden from iteration
configurable: false
}
});
console.log(Object.keys(product)); // ["name", "sku"] — _internalId hidden
console.log(product._internalId); // "abc123" — still accessible directlyDeleting Properties
The delete Operator
The delete operator removes a property from an object and returns true if successful:
const user = {
name: "Alice",
age: 28,
email: "alice@example.com",
tempToken: "abc123"
};
// Remove a property
delete user.tempToken;
console.log(user);
// { name: "Alice", age: 28, email: "alice@example.com" }
// Verify removal
console.log("tempToken" in user); // false
console.log(user.tempToken); // undefineddelete with Bracket Notation
const settings = {
"theme-color": "#333",
"font-size": "16px",
"show-sidebar": true
};
delete settings["show-sidebar"];
console.log(settings);
// { "theme-color": "#333", "font-size": "16px" }delete Behavior and Return Values
const obj = { a: 1, b: 2 };
console.log(delete obj.a); // true (property removed)
console.log(delete obj.c); // true (property didn't exist, still returns true)
console.log(delete obj); // false (can't delete variables)
// Non-configurable properties can't be deleted
Object.defineProperty(obj, "fixed", {
value: 42,
configurable: false
});
console.log(delete obj.fixed); // false (non-configurable)
console.log(obj.fixed); // 42 (still there)Removing Properties Without delete (Destructuring)
Using destructuring with rest syntax creates a new object without the unwanted properties, leaving the original unchanged:
const user = {
name: "Alice",
age: 28,
password: "secret123",
ssn: "123-45-6789"
};
// Remove sensitive fields immutably
const { password, ssn, ...safeUser } = user;
console.log(safeUser);
// { name: "Alice", age: 28 }
console.log(user);
// { name: "Alice", age: 28, password: "secret123", ssn: "123-45-6789" } — unchangedPreventing Property Changes
Object.freeze()
Object.freeze() prevents all changes to an object: no adding, updating, or deleting properties:
const config = Object.freeze({
apiUrl: "https://api.example.com",
timeout: 5000,
maxRetries: 3
});
config.timeout = 10000; // Silently fails
config.newProp = "value"; // Silently fails
delete config.apiUrl; // Returns false
console.log(config.timeout); // 5000 (unchanged)Object.seal()
Object.seal() allows updating existing properties but prevents adding or deleting them:
const user = Object.seal({
name: "Alice",
age: 28
});
user.age = 29; // Works: updating existing property
user.email = "a@b.com"; // Silently fails: can't add
delete user.name; // Returns false: can't delete
console.log(user); // { name: "Alice", age: 29 }Immutability Comparison
| Method | Add Properties | Update Properties | Delete Properties |
|---|---|---|---|
| Normal object | Yes | Yes | Yes |
Object.seal() | No | Yes | No |
Object.freeze() | No | No | No |
Object.preventExtensions() | No | Yes | Yes |
Checking Property Modification Status
const frozen = Object.freeze({ a: 1 });
const sealed = Object.seal({ b: 2 });
const normal = { c: 3 };
console.log(Object.isFrozen(frozen)); // true
console.log(Object.isSealed(sealed)); // true
console.log(Object.isExtensible(normal)); // true
console.log(Object.isExtensible(frozen)); // falseCommon Mistakes to Avoid
Setting Properties on const Objects
Using const with objects prevents reassignment of the variable, not modification of the object itself:
const user = { name: "Alice" };
// This works fine — modifying the object, not reassigning the variable
user.age = 28;
user.name = "Bob";
console.log(user); // { name: "Bob", age: 28 }
// This fails — reassigning the variable
// user = { name: "Charlie" }; // TypeError: Assignment to constant variableShallow Freeze Gotcha
Object.freeze() only freezes the top level. Nested objects remain mutable:
const settings = Object.freeze({
theme: "dark",
notifications: {
email: true,
push: false
}
});
// Top-level: frozen
settings.theme = "light"; // Silently fails
// Nested: NOT frozen!
settings.notifications.push = true; // Works!
console.log(settings.notifications.push); // true
// Deep freeze function
function deepFreeze(obj) {
Object.freeze(obj);
for (const value of Object.values(obj)) {
if (typeof value === "object" && value !== null) {
deepFreeze(value);
}
}
return obj;
}Rune AI
Key Insights
- Adding properties: Use dot or bracket notation for single additions and
Object.assignor spread for multiple - Updating properties: Same assignment syntax as adding; the key already exists so the value gets replaced
- Deleting properties: Use
delete obj.keyfor mutation or destructuring rest for immutable removal - Object.defineProperty: Controls writability, enumerability, and configurability for individual properties
- Immutability: Use
Object.freezeto prevent all changes andObject.sealto allow updates but block additions and deletions
Frequently Asked Questions
Does delete actually free memory in JavaScript?
Why does delete return true for nonexistent properties?
Should I use delete or set to undefined to remove properties?
Can I add properties to frozen objects?
What is the difference between Object.assign and spread for adding properties?
Conclusion
JavaScript objects are inherently mutable, giving you full control to add, update, and delete properties at any time. Simple dot and bracket notation handles most cases, while Object.defineProperty provides fine-grained control over property behavior. When you need stability, Object.freeze, Object.seal, and immutable spread patterns prevent accidental mutations. Understanding both the mutable and immutable approaches lets you pick the right strategy for each situation in your code.
More in this topic
OffscreenCanvas API in JS for UI Performance
Master the OffscreenCanvas API to offload rendering from the main thread. Covers worker-based 2D and WebGL rendering, animation loops inside workers, bitmap transfer, double buffering, chart rendering pipelines, image processing, and performance measurement strategies.
Advanced Web Workers for High Performance JS
Master Web Workers for truly parallel JavaScript execution. Covers dedicated and shared workers, structured cloning, transferable objects, SharedArrayBuffer with Atomics, worker pools, task scheduling, Comlink RPC patterns, module workers, and performance profiling strategies.
JavaScript Macros and Abstract Code Generation
Master JavaScript code generation techniques for compile-time and runtime metaprogramming. Covers AST manipulation, Babel plugin authorship, tagged template literals as macros, code generation pipelines, source-to-source transformation, compile-time evaluation, and safe eval alternatives.