JavaScript: Objects
Learn to create, access, modify, and transform objects using every built-in method, destructuring, spread, getters, setters, and the this keyword.
Object Creation
Objects are most commonly created with the object literal syntax {}. The new Object() constructor exists but is verbose and rarely used. Object.create() gives fine control over the prototype.
Object Creation Methods
Three ways to create objects. The literal syntax is the standard for everyday use.
- Literal:
const obj = { key: value }, preferred - Constructor:
const obj = new Object(), avoid, verbose - Object.create(proto): creates an object with a specific prototype
- Factory function: a regular function that returns a new object literal
Object Creation
Factory functions are a simple way to create many similar objects without classes.
Press Run to execute the code and see output here.
Object Properties
An object is a collection of key-value pairs. Keys are strings (or Symbols) and values can be any type including other objects or functions. Properties defined at creation time sit directly on the object.
Key-Value Pairs
Every property has a string key and a value of any type.
- Keys are strings by default, quotes optional when the key is a valid identifier
- Values can be primitives, arrays, objects, or functions
- A property whose value is a function is called a method
- Keys with spaces or special characters require quotes:
"first-name": "Alice"
Object Properties
Keys with hyphens or spaces must be quoted and accessed with bracket notation.
Press Run to execute the code and see output here.
Accessing Properties
There are two ways to read a property: dot notation for known keys and bracket notation for dynamic keys or keys that are not valid identifiers. Accessing a property that does not exist returns undefined.
Dot vs Bracket Notation
Use dot notation by default. Use bracket notation when the key is dynamic or contains special characters.
- Dot:
obj.name, clean, works when key is a valid identifier - Bracket:
obj["name"]orobj[variable], required for dynamic keys - Missing property returns
undefined, not an error - Optional chaining
obj?.propprevents TypeError on null/undefined objects
Dot vs Bracket Notation
user[key] where key is a variable is the most common reason to reach for bracket notation.
Press Run to execute the code and see output here.
Adding and Modifying Properties
Properties can be added or updated at any time simply by assigning to a key. If the key exists, the value is overwritten. If it does not exist, a new property is created.
Adding and Updating
Assignment creates or overwrites. const on the object prevents rebinding, not mutation.
obj.newKey = value: adds if absent, updates if present- Computed keys in literals:
{ [varName]: value } constprevents reassigning the variable, not changing its properties- Use
Object.freeze()if you want to block mutations entirely
Adding and Modifying Properties
Computed keys [field] let you use a variable as a property name at creation time.
Press Run to execute the code and see output here.
Deleting Properties
The delete operator removes a property from an object. After deletion, accessing the key returns undefined and the key no longer appears in Object.keys().
delete Operator
Removes a property entirely. Setting to undefined leaves the key present.
delete obj.key: removes the property and its key- Returns
trueon success (even if the property never existed) - Cannot delete non-configurable properties
- Prefer object spread for immutable removal:
const { key, ...rest } = obj
Deleting Properties
Spread destructuring is the immutable way to remove a property without modifying the original.
Press Run to execute the code and see output here.
Checking Property Existence
Two tools check whether a property exists: the in operator and Object.hasOwn(). They differ in whether they search the prototype chain.
in vs hasOwn
Use hasOwn for own properties only. Use in when inherited properties should also match.
"key" in obj: true for own and inherited enumerable propertiesObject.hasOwn(obj, "key"): true for own properties only (ES2022)obj.hasOwnProperty("key"): same as hasOwn but can be overridden- Never check with
obj.key !== undefined: the property may exist but be set to undefined
in vs Object.hasOwn
toString is inherited so 'in' finds it but hasOwn does not. Always use hasOwn for own-property checks.
Press Run to execute the code and see output here.
Object.keys(), Object.values(), Object.entries()
These three static methods return arrays of the object's own enumerable properties, making objects iterable with standard array methods.
keys / values / entries
Convert object data into arrays so you can use map, filter, and forEach.
Object.keys(obj): array of property namesObject.values(obj): array of property valuesObject.entries(obj): array of[key, value]pairs- All three return only own enumerable properties
- Pair entries with
Object.fromEntries()to convert back to an object
keys, values, entries
entries + fromEntries is the pattern for transforming object values immutably.
Press Run to execute the code and see output here.
Object.assign()
Object.assign(target, ...sources) copies own enumerable properties from one or more source objects into the target. It is a shallow copy, nested objects are still shared references.
Object.assign()
Shallow merge into target. For immutable merging, prefer spread: { ...a, ...b }.
- Mutates the target object: pass
{}as first arg to avoid side effects - Later sources overwrite earlier ones for the same key
- Shallow: nested objects are copied by reference, not cloned
- Spread
{ ...a, ...b }is equivalent and does not mutate anything
Object.assign()
Always pass {} as the first argument to avoid mutating a source. Watch out for shared nested references.
Press Run to execute the code and see output here.
Object.freeze() and Object.seal()
freeze() makes an object fully immutable. seal() allows updating existing properties but blocks adding or deleting. Both are shallow.
freeze vs seal
freeze blocks everything. seal blocks add/delete but allows updates.
Object.freeze(obj): no add, no delete, no updateObject.seal(obj): no add, no delete, but existing values can change- Both are shallow: nested objects are not frozen/sealed automatically
- Mutations silently fail in non-strict mode; throw in strict mode
Object.isFrozen(obj)/Object.isSealed(obj)to check status
freeze and seal
freeze prevents all changes. seal allows updating existing values but nothing else.
Press Run to execute the code and see output here.
Object.create()
Object.create(proto) creates a new object whose internal prototype is set to proto. This is the low-level mechanism behind prototype-based inheritance.
Object.create()
Creates an object that inherits from a specific prototype, useful for prototypal patterns.
Object.create(proto): new object with proto as its prototypeObject.create(null): pure object with no prototype at all- Properties defined on proto are shared across all created objects
- Classes and constructor functions use this mechanism internally
Object.create()
Object.create(null) creates a true dictionary with no prototype baggage, useful for lookup tables.
Press Run to execute the code and see output here.
Nested Objects
Object properties can themselves be objects, creating nested structures. Access nested values by chaining dot or bracket notation. Use optional chaining ?. to safely navigate paths that may not exist.
Nested Objects
Chain dot notation to reach deep properties. Use ?. to avoid TypeError on missing paths.
- Access:
obj.address.city - Safe access:
obj?.address?.cityreturns undefined instead of throwing - Modifying a nested value mutates the original: clone deeply if needed
- Deep clone:
JSON.parse(JSON.stringify(obj))for plain data (no functions/Dates)
Nested Objects
JSON.parse(JSON.stringify(obj)) deep clones plain objects but strips functions and Date values.
Press Run to execute the code and see output here.
Object Destructuring
Object destructuring extracts properties into variables in one concise statement. Unlike array destructuring, it matches by key name, not position.
Object Destructuring
Unpack properties into variables by name. Rename, set defaults, and use rest all in one line.
- Syntax:
const { name, age } = user; - Rename:
const { name: fullName } = user; - Default value:
const { role = "viewer" } = user; - Nested:
const { address: { city } } = user; - Rest:
const { name, ...rest } = user;
Object Destructuring
Rename with a colon: { name: fullName } reads as 'take name, call it fullName'.
Press Run to execute the code and see output here.
Spread Operator with Objects
The spread operator ... copies own enumerable properties into a new object. It is the immutable alternative to Object.assign() and produces no side effects.
Object Spread
Copy and merge objects immutably. Later properties overwrite earlier ones.
- Clone:
const copy = { ...obj }; - Merge:
const merged = { ...a, ...b }; - Override a specific key:
{ ...user, role: "admin" } - Shallow: nested objects are still shared references
Object Spread
{ ...user, role: 'admin' } is the clean pattern for updating one field without mutation.
Press Run to execute the code and see output here.
this Keyword in Objects
Inside an object method, this refers to the object the method is called on. Arrow functions do not have their own this: they inherit it from the surrounding scope, which makes them unsuitable as object methods.
this in Object Methods
this is the object to the left of the dot at call time. Arrow functions inherit this from outside.
- Regular method:
thisrefers to the calling object - Arrow function as method:
thisis from the outer scope, not the object thisis determined at call time, not definition time- Destructured method loses
this: bind or use an arrow wrapper if needed
this in Object Methods
Never use arrow functions as object methods when you need to access the object via this.
Press Run to execute the code and see output here.
Getters and Setters
Getters and setters define computed or validated properties using get and set keywords. They look like plain properties to the caller but run a function behind the scenes.
get / set
Properties that run logic on read or write, without the caller knowing.
get propName(): called when the property is readset propName(value): called when the property is assigned- Getters have no parameters; setters have exactly one
- Use for computed values, validation, or lazy initialisation
Getters and Setters
fullName looks like a plain property but runs a function on every read and write.
Press Run to execute the code and see output here.
Object Shorthand Notation
ES6 introduced shorthand syntax for two common patterns: property shorthand (when the variable name matches the key) and method shorthand (omitting function keyword from methods).
Shorthand Notation
Write less when the variable name already matches the property name.
- Property shorthand:
{ name }instead of{ name: name } - Method shorthand:
greet() { }instead ofgreet: function() { } - Computed shorthand:
{ [key]: value }, dynamic key at creation - Commonly used in function return values and React props
Object Shorthand
Returning this from methods enables fluent chaining: calc.add(5).add(3).reset().
Press Run to execute the code and see output here.
Knowledge Check
1. Which notation must you use to access a property whose name is stored in a variable?
2. What does Object.freeze() do?
3. What does Object.entries() return?
4. What does the "in" operator check?
5. What does Object.assign() do?
6. What does "this" refer to inside an object method when called normally?
7. What is shorthand property notation?
8. What does Object.create(proto) do?