Objects: values with names
An array holds things in order. An object holds them by name, which is what you want the moment the parts are different in kind — a student has a name, a roll number and a set of marks, and "the second one" is a useless way to ask for any of them.
const student = {
name: "Ayesha",
rollNumber: 42,
hasPaid: true,
};
console.log(student.name);
console.log(student.rollNumber);
Ayesha 42
Each key: value pair is a property. Keys are strings; the quotes are
optional when the key is a valid identifier, which is why name needs none.
Dot and bracket
const stock = { chai: 12, "green tea": 4 };
console.log(stock.chai);
console.log(stock["chai"]);
console.log(stock["green tea"]);
12 12 4
Dot notation is shorter and what you will use most. Brackets are required in
two cases: when the key is not a valid identifier — "green tea" has a space —
and when the key is in a variable.
const stock = { chai: 12, coffee: 4 };
const wanted = "coffee";
console.log(stock[wanted]);
console.log(stock.wanted);
4 undefined
stock.wanted looked for a property literally called wanted, found none, and
returned undefined. That is the single most common object mistake: dot
notation takes the name you typed, brackets take the value of the expression.
Missing properties are undefined, not an error
const student = { name: "Ayesha" };
console.log(student.email);
console.log(student.email === undefined);
console.log("email" in student);
undefined true false
Reading something that is not there gives undefined quietly. That is
forgiving right up until you read a property of that undefined:
const student = { name: "Ayesha" };
console.log(student.address.city);
That throws:
TypeError: Cannot read properties of undefined (reading 'city')
student.address is undefined, and undefined has no city. This is one of
the most frequent runtime errors in JavaScript, and the message names the
property that failed — read it carefully, because the culprit is the property
before the one it mentions.
Optional chaining
?. stops and returns undefined instead of throwing.
const student = { name: "Ayesha" };
console.log(student.address?.city);
console.log(student.address?.city ?? "No address on file");
undefined No address on file
Pairing ?. with ?? reads as "the city if there is one, otherwise this".
Use it where the value is legitimately optional — not everywhere, because
silencing a TypeError that indicates a real bug just moves the failure
somewhere less helpful.
Changing, adding, removing
Objects are mutable, and const does not stop that.
const student = { name: "Ayesha", marks: 72 };
student.marks = 88;
student.email = "a@example.com";
delete student.marks;
console.log(student);
{ name: 'Ayesha', email: 'a@example.com' }const prevents reassigning the name, not changing what it points at.
student = {} would throw; student.marks = 88 is fine. The same is true of
arrays, and it surprises everyone once.
Nesting
Real data is objects inside objects inside arrays.
const order = {
customer: { name: "Bilal", phone: "0300-1234567" },
items: [
{ name: "chai", qty: 2, price: 50 },
{ name: "paratha", qty: 1, price: 120 },
],
};
console.log(order.customer.name);
console.log(order.items[0].name);
console.log(order.items.length);
console.log(order.items.reduce((sum, i) => sum + i.qty * i.price, 0));
Bilal chai 2 220
Read the path left to right: order, its items, the first one, its name.
Every dot and bracket is one step, and when it fails the error tells you which
step returned undefined.
Shorthand and computed keys
const name = "Sana";
const marks = 88;
const field = "grade";
const student = { name, marks, [field]: "A" };
console.log(student);
{ name: 'Sana', marks: 88, grade: 'A' }{ name } is shorthand for { name: name } — when the variable is already
called what you want the key to be. Square brackets around a key make it
computed: the value of field becomes the key.
Methods
A function stored on an object is a method. this refers to the object it was
called on.
const counter = {
count: 0,
increment() {
this.count = this.count + 1;
return this.count;
},
};
console.log(counter.increment());
console.log(counter.increment());
1 2
One warning while you are here: do not use an arrow function for a method
that needs this. An arrow does not get its own this, so it looks outward
to the surrounding scope instead of at the object:
const broken = {
count: 5,
show: () => this?.count,
};
console.log(broken.show());
undefined
The shorthand increment() { } form is the one to use.
Looping over an object
const stock = { chai: 12, coffee: 4 };
console.log(Object.keys(stock));
console.log(Object.values(stock));
console.log(Object.entries(stock));
for (const [item, count] of Object.entries(stock)) {
console.log(`${item}: ${count}`);
}
[ 'chai', 'coffee' ] [ 12, 4 ] [ [ 'chai', 12 ], [ 'coffee', 4 ] ] chai: 12 coffee: 4
Object.entries turns the object into an array of [key, value] pairs, which
means every array method from the last lesson applies:
const stock = { chai: 12, coffee: 4, lassi: 0 };
const inStock = Object.entries(stock)
.filter(([, count]) => count > 0)
.map(([item]) => item);
console.log(inStock);
[ 'chai', 'coffee' ]
That [, count] skips the first element of the pair — a comma with nothing
before it. It is legal and worth recognising.
Copying is shallow
const original = { name: "Ayesha", marks: { maths: 72 } };
const copy = { ...original };
copy.name = "Bilal";
copy.marks.maths = 100;
console.log(original.name);
console.log(original.marks.maths);
Ayesha 100
The name is independent; the marks are not. { ...original } copies the
top level only, so copy.marks and original.marks are the same nested
object, and changing one changes both.
For a fully independent copy of nested data:
const original = { name: "Ayesha", marks: { maths: 72 } };
const deep = structuredClone(original);
deep.marks.maths = 100;
console.log(original.marks.maths);
console.log(deep.marks.maths);
72 100
structuredClone is built in and handles nesting, dates and arrays. The old
JSON.parse(JSON.stringify(obj)) trick also works and quietly destroys dates,
undefined and functions on the way through.
Comparing objects
const a = { name: "Ayesha" };
const b = { name: "Ayesha" };
console.log(a === b);
console.log(a === a);
console.log(a.name === b.name);
false true true
=== on objects asks "are these the same object?", not "do they contain the
same things?". Two objects built identically are still two objects. To compare
contents you compare the properties you care about, or use a library.
Objects versus arrays
- Order matters, items are alike → array.
- Named parts, different in kind → object.
- A collection of records → an array of objects, which is what most real data is.
Next: destructuring and spread, which is the shorthand for taking objects apart and putting them back together.
Prefer learning with a teacher?
WasiLearn Academy runs small-group classes covering this material, online and in Karachi.
See the classes