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() { // same as bark: function () {}
4 return `${this.name} says woof`;
5 },
6};
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() { this.n++; return this; },
4};
5
6counter.inc().inc().inc();
7counter.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.










