JAVASCRIPT / OBJECTS AND PROPERTY MECHANICS
Reading, writing, and deleting properties
Read, add, update, and remove object properties deliberately, and tell an absent key apart from one whose stored value is undefined.
What you will learn
- Read missing keys safely: obj.miss is undefined; use ?. to survive missing objects
- Distinguish an absent key from a stored undefined with Object.hasOwn or the in operator
- Add or update with plain assignment, knowing it always creates an own property
- Remove a key with delete; assigning undefined keeps it in Object.keys and in spreads
Understanding Reading, writing, and deleting properties
An object is a live mapping from keys to values, where every key is a string or a symbol. Reading is a lookup: book.title and book["title"] ask exactly the same question and differ only in whether you spell the key in source code or compute it as a value. A lookup that finds nothing evaluates to undefined instead of throwing, which is why a misspelled property name fails quietly rather than loudly. That one design choice is behind most of the confusion in this lesson.
Assignment does double duty: obj.k = v replaces the value when the key already exists and creates the entry when it does not, so there is no separate add operation to learn. The detail that matters is that assignment always writes to the object you named, never to its prototype, so if an object inherits fontSize from a defaults object, prefs.fontSize = 16 creates a brand-new own property that shadows the inherited one. Reads walk up the prototype chain, writes stop at the first object, and that asymmetry explains most surprising results.
delete obj.k is the only operation that removes the key itself, and that is not the same as obj.k = undefined. Both make a later read return undefined, but after the assignment the key is still present: it shows up in Object.keys, "k" in obj is true, and spreading the object over a set of defaults will overwrite them with undefined. Reach for Object.hasOwn(obj, "k") or the in operator when you need to know whether the entry exists at all, and note that delete returns true whenever the key is gone afterwards, including when it was never there, so its return value is not a report of what it did.
const book = { title: "Dune", pages: 412 };
console.log(book.title);
console.log(book.author);
book.author = "Frank Herbert";
book.pages = 604;
console.log(book.author, book.pages);
book.rating = undefined;
console.log("rating" in book, book.rating);
console.log(delete book.rating);
console.log("rating" in book);
console.log(delete book.nothingHere);
console.log(Object.keys(book).join(", "));Reading is a lookup that can miss without error, writing always creates or replaces an own entry, and only delete removes the key itself.
Worked examples
Missing property versus missing object
Shows that reading an absent key is harmless but reading through an absent object is a TypeError.
const config = { server: { host: "localhost" } };
console.log(config.server.host);
console.log(config.server.port ?? 8080);
console.log(config.database?.host);
try {
config.database.host;
} catch (err) {
console.log(err.name);
}Example explained
Line 1config.server.port is a genuine lookup on an existing object that misses, so it is undefined and ?? supplies 8080.
Line 2config.database?.host short-circuits: because config.database is undefined, the whole expression is undefined and the .host lookup never runs.
Line 3Without ?., the engine tries to read host from undefined; V8 words the thrown message "Cannot read properties of undefined (reading 'host')".
Line 4So the safety of a read depends on the object in front of the dot, not on whether the final key exists.
Writing shadows, deleting unshadows
Demonstrates that assignment creates an own property and delete only removes own properties.
const defaults = { theme: "light", fontSize: 14 };
const userPrefs = Object.create(defaults);
userPrefs.theme = "dark";
console.log(userPrefs.theme, userPrefs.fontSize);
console.log(Object.hasOwn(userPrefs, "theme"), Object.hasOwn(userPrefs, "fontSize"));
console.log("fontSize" in userPrefs);
delete userPrefs.theme;
console.log(userPrefs.theme);
delete userPrefs.fontSize;
console.log(userPrefs.fontSize);Example explained
Line 1Object.create(defaults) gives userPrefs no own keys, so both initial reads resolve on the prototype.
Line 2userPrefs.theme = "dark" writes an own entry and leaves defaults.theme untouched, which is why hasOwn is true for theme and false for fontSize.
Line 3"fontSize" in userPrefs is true because in searches the whole chain, unlike Object.hasOwn.
Line 4delete userPrefs.theme removes only the own entry, so the inherited "light" becomes visible again, and deleting fontSize does nothing at all.
delete on an array leaves a hole
Shows why delete is the wrong tool for removing array elements.
const scores = [10, 20, 30];
delete scores[1];
console.log(scores.length);
console.log(scores[1]);
console.log(1 in scores);
console.log(scores.join("|"));
const fixed = [10, 20, 30];
fixed.splice(1, 1);
console.log(fixed.length, fixed.join("|"));Example explained
Line 1An array index is just a property key, so delete scores[1] removes the key "1" but never adjusts length, which stays 3.
Line 21 in scores is false, which is exactly how a hole differs from an element that holds undefined.
Line 3join renders the hole as an empty string, producing 10||30 instead of 10|30.
Line 4splice removes the entry and reindexes the rest, so length drops to 2, which is what element removal normally means.
Important notes
Every key is coerced to a string (or kept as a symbol), so obj[1] and obj["1"] are the same slot and obj[{ id: 1 }] writes to the key "[object Object]", where every object-keyed write lands on top of the previous one.
delete is not guaranteed to succeed: on a non-configurable property it returns false in sloppy mode and throws a TypeError in strict mode, which includes all module code.
Common mistakes
Writing obj.key = undefined to remove a property: the key survives, so "key" in obj is still true, for...in still visits it, and { ...defaults, ...obj } overwrites a good default with undefined.
Using dot notation with a variable, as in obj.key where key = "title": this reads the literal property named "key", silently returns undefined, and on the left side of = creates a junk property called key.
Calling delete on array elements to shrink a list: length does not change, the slot becomes a hole, and index-based loops then hand undefined to code that expected a number.
Try it yourself
Change, predict, then run
In a browser console, build const settings = { volume: 5, muted: false, label: undefined } and write remove(obj, key) that returns true only if the key existed before deleting it. Call it with "volume", "label", and "bitrate", logging the return value and Object.keys(settings) after each call.
Open the JavaScript workspaceCheck your understanding
After const base = { color: "red" }; const item = Object.create(base); item.color = "blue"; delete item.color; what does console.log(item.color, Object.hasOwn(item, "color")) print?
- undefined false
- red false
- blue true
- red true
Show answer
The assignment created an own property on item that shadowed base.color, and delete removed that own entry only, so the lookup continues up the prototype chain and finds "red" while Object.hasOwn reports false. "undefined false" assumes delete erased the name for the whole chain, but delete never touches a prototype's properties.