JavaScript Objects — The Basics

A practical introduction to JavaScript objects — literals, property access, shorthand syntax, computed keys, methods, mutation by reference, and the difference between value semantics and reference semantics.

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

Checking whether a property exists

TestBehaviour
"key" in objTrue 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 !== undefinedFast 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:

HelperReturns
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

NeedTool
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 cloneHand-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.

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/entries are the modern iteration tools.
  • Objects are reference values — clone before you mutate. structuredClone handles deep clones of plain data.
  • Inside a method, this is the receiver — return it to enable chaining.

Next: Prototypes and the prototype chain — the model on which every JavaScript object is built.