What is an object in JavaScript?
In JavaScript an object is an unordered collection of string-keyed (or symbol-keyed) properties. Almost everything that is not a primitive is an object — arrays, functions, dates, regular expressions, Map, Set, the global object, even Math.
This is the first of four posts on objects. It covers the foundations. The series continues with:
Object literals
The literal form { ... } is the default tool. It is concise, fast to parse, and supports several pieces of syntactic sugar.
1const book = {
2 title: 'Eloquent JavaScript',
3 author: 'Marijn Haverbeke',
4 year: 2018
5}
Property shorthand
When a property name matches the variable being assigned, you can omit the colon:
1const title = 'Eloquent JavaScript'
2const year = 2018
3
4const book = { title, year } // same as { title: title, year: year }
Method shorthand
1const dog = {
2 name: 'Rex',
3 bark() {
4 // same as bark: function () {}
5 return `${this.name} says woof`
6 }
7}
Computed keys
Use any expression as the key by wrapping it in []:
1const field = 'email'
2const user = { [field]: 'ali@example.com' }
3// user.email === "ali@example.com"
Two ways to access a property
1book.title // dot notation — clean, but key must be a valid identifier
2book['title'] // bracket notation — accepts any string or symbol expression
Bracket notation is required for dynamic keys, keys with hyphens or spaces, and numeric keys:
1const key = 'title'
2book[key]
3
4const config = { 'max-retries': 3 }
5config['max-retries']
Adding, updating, and removing properties
1const user = { name: 'Ali' }
2
3user.email = 'ali@example.com' // add
4user.name = 'Ali S.' // update
5delete user.email // remove
Note
deleteworks on own properties of an object. It is rarely needed in modern code — using a fresh object via spread is usually clearer:1const { email, ...withoutEmail } = user
Checking whether a property exists
| Test | Behaviour |
|---|---|
"key" in obj | True if the property exists on obj or its prototype chain. |
Object.hasOwn(obj, "key") (ES2022) | True only if obj has the property as an own property. |
obj.key !== undefined | Fast but wrong if the value is legitimately undefined. |
Object.hasOwn is the modern replacement for the awkward Object.prototype.hasOwnProperty.call(obj, "key") pattern.
Iterating properties
Most code wants own, enumerable, string-keyed properties:
| Helper | Returns |
|---|---|
Object.keys(obj) | An array of own enumerable string keys. |
Object.values(obj) | An array of the corresponding values. |
Object.entries(obj) | An array of [key, value] pairs. |
1const scores = { ali: 90, omar: 72, zaid: 85 }
2
3for (const [name, score] of Object.entries(scores)) {
4 console.log(`${name}: ${score}`)
5}
6
7const total = Object.values(scores).reduce((a, b) => a + b, 0)
Avoid for...in unless you specifically want to walk the prototype chain too — and even then, gate the body with Object.hasOwn.
Reference semantics — the rule that explains every mutation bug
Objects are passed and assigned by reference. Two variables that point to the same object see each other’s changes:
1const a = { count: 1 }
2const b = a
3b.count = 99
4console.log(a.count) // 99
Equality compares references, not contents:
1{ x: 1 } === { x: 1 }; // false — two different objects
To compare by value, walk both objects manually, JSON-stringify them (cheap but limited), or use a structural equality helper such as Lodash’s isEqual.
Cloning
| Need | Tool |
|---|---|
| Shallow clone of own enumerable string props | { ...source } or Object.assign({}, source) |
| Deep clone of plain data (incl. Maps, Sets, Dates, typed arrays) | structuredClone(source) (built in since 2022) |
| Domain-aware deep clone | Hand-written or a library that understands your classes |
1const original = { user: { name: 'Ali' }, tags: ['a', 'b'] }
2
3const shallow = { ...original }
4shallow.user.name = 'Omar'
5console.log(original.user.name) // "Omar" — nested object is shared!
6
7const deep = structuredClone(original)
8deep.user.name = 'Zaid'
9console.log(original.user.name) // unchanged
Object spread and rest
Spread copies own enumerable string-keyed properties into a new object. Later sources win on collision — perfect for “patch” semantics:
1const defaults = { method: 'GET', retries: 3 }
2const request = { ...defaults, retries: 1 }
3// { method: "GET", retries: 1 }
Rest in destructuring captures the “everything else”:
1const user = { id: 1, name: 'Ali', email: 'ali@example.com' }
2const { id, ...rest } = user
3// id === 1
4// rest === { name: "Ali", email: "ali@example.com" }
Methods and this
Inside a method, this is the object the method was called on:
1const counter = {
2 n: 0,
3 inc() {
4 this.n++
5 return this
6 }
7}
8
9counter.inc().inc().inc()
10counter.n // 3
Returning this enables method chaining — the pattern behind jQuery, D3, and most builder APIs.
Important
Detaching a method from its object loses the binding. The advanced functions post covers the four
thisrules in detail.
Summary
- An object is a collection of string- or symbol-keyed properties.
- Prefer the literal form
{}with shorthand, methods, and computed keys. - Use bracket notation for dynamic or non-identifier keys.
Object.hasOwn,Object.keys/values/entriesare the modern iteration tools.- Objects are reference values — clone before you mutate.
structuredClonehandles deep clones of plain data. - Inside a method,
thisis the receiver — return it to enable chaining.
Next: Prototypes and the prototype chain — the model on which every JavaScript object is built.










