RizTech Academy logo
RizTech Academy
Objects and ArraysLesson 1 of 725 min

Objects, properties and methods

So far your data has been loose variables: a customer here, a plate count there. Real data arrives in clumps — one order has a customer, a count, an address and a status, and those belong together. An object is that clump.

Making one

const order = {
  customer: 'Priya',
  plates: 3,
  paid: true,
};

console.log(order.customer);
console.log(order.plates * 80);
Priya
240

Keys on the left, values on the right, commas between. A value can be anything — a number, a string, an array, another object, a function.

Two ways to reach a property

console.log(order.customer);
console.log(order['customer']);

Dot notation is what you will write. Brackets are for two cases dot cannot handle.

Keys that are not valid names:

const order = { 'delivery note': 'ring twice' };
console.log(order['delivery note']);
ring twice

Keys you do not know until the code runs:

const key = 'plates';
console.log(order[key]);
3

That distinction matters. order[key] looks up whatever key contains; order.key looks for a property literally named key. Getting undefined from a lookup that should have worked is very often this.

Missing properties give undefined

console.log(order.discount);
undefined

Not an error. This is the friendly part — until you go one level further:

console.log(order.address.city);
TypeError: Cannot read properties of undefined (reading 'city')

order.address is undefined, and undefined has no city. This is the single most common runtime error in JavaScript, and it has a dedicated fix which is two lessons away.

To ask whether a property exists:

console.log('customer' in order);
console.log(order.customer !== undefined);

in is the honest answer, because a property can exist and hold undefined.

Changing things

const order = { customer: 'Priya', plates: 3 };

order.plates = 5;
order.status = 'delivered';
delete order.plates;

console.log(order);
{ customer: 'Priya', status: 'delivered' }

All of that on a const, because — module 2 — const protects the binding, not the contents.

Methods

A function stored on an object is a method:

const order = {
  plates: 3,
  rate: 80,
  total() {
    return this.plates * this.rate;
  },
};

console.log(order.total());
240

this is the object, because of the dot. Module 3 covered exactly why, and why this must not be an arrow function.

Shorthand

When a variable and the key share a name:

const customer = 'Priya';
const plates = 3;

const order = { customer, plates };
console.log(order);
{ customer: 'Priya', plates: 3 }

You will see this constantly. Computed keys use brackets:

const field = 'status';
const update = { [field]: 'delivered' };
console.log(update);
{ status: 'delivered' }

Without the brackets the key would literally be field.

Walking an object

Three functions turn an object into an array so you can loop it:

const order = { customer: 'Priya', plates: 3, paid: true };

console.log(Object.keys(order));
console.log(Object.values(order));
console.log(Object.entries(order));
[ 'customer', 'plates', 'paid' ]
[ 'Priya', 3, true ]
[ [ 'customer', 'Priya' ], [ 'plates', 3 ], [ 'paid', true ] ]

Object.entries with destructuring is the readable way to loop:

for (const [key, value] of Object.entries(order)) {
  console.log(`${key}: ${value}`);
}
customer: Priya
plates: 3
paid: true

Destructuring is the fourth lesson of this module; for now it is the same square brackets you saw with .entries() on an array.

These replace for...in from module 2, and they are better: they only give you the object's own properties, with no inherited surprises.

Keys are always strings

const weird = {};
weird[1] = 'number one';
weird['1'] = 'string one';

console.log(weird);
{ '1': 'string one' }

One property, not two. Object keys are converted to strings, so 1 and '1' are the same key and the second write overwrote the first. If you need real number keys — or object keys — you need a Map, which is lesson six.

The trap: objects are handled by reference

This is the most important thing in the lesson.

const a = { plates: 3 };
const b = a;

b.plates = 9;
console.log(a.plates);
9

b = a did not copy the object. It copied a reference to it — both names point at one object, and changing it through either name changes the same thing.

This also explains why two identical-looking objects are never equal:

console.log({} === {});
false

=== on objects asks "are these the same object", not "do they look alike". Two separate empty objects are two objects.

To make an actual copy, spread it:

const original = { plates: 3 };
const copy = { ...original };

copy.plates = 9;
console.log(original.plates);
3

But that copy is shallow — one level deep only:

const base = { customer: 'Priya', address: { city: 'Pune' } };
const clone = { ...base };

clone.address.city = 'Mumbai';
console.log(base.address.city);
Mumbai

The outer object was copied; address was not — both objects point at the same inner one. This bug is hard to find because the copy looked like it worked.

For a genuinely deep copy:

const fresh = { customer: 'Priya', address: { city: 'Pune' } };
const deep = structuredClone(fresh);

deep.address.city = 'Nagpur';
console.log(fresh.address.city);
Pune

structuredClone is built into every current browser and Node. It cannot copy functions, and it will refuse if the object contains any.

Check your work

order['delivery note'] works and order.delivery note is a syntax error. Brackets handle keys that are not valid identifiers, and keys held in variables.

order[key] where key is 'plates' gives 3; order.key gives undefined, because the second looks for a property actually named key.

A missing property is undefined, not an error. Going one level deeper is an error: TypeError: Cannot read properties of undefined (reading 'city').

You can add, change and delete properties on a const object. const stops the name being repointed, nothing more.

weird[1] and weird['1'] are the same property. Object keys are strings, so the object ends up { '1': 'string one' } with one key.

b = a then b.plates = 9 changes a too. Objects are held by reference.

{} === {} is false. Equality on objects asks whether they are the same object.

{ ...original } copies one level. A nested object is shared, so clone.address.city = 'Mumbai' changes base as well. structuredClone copies all the way down.

Object.keys, .values and .entries give ['customer', 'plates', 'paid'], ['Priya', 3, true] and the pairs.

updateOrder needs to know about the nested level:

function updateOrder(order, changes) {
  const updated = { ...order, ...changes };
  if (changes.address) {
    updated.address = { ...order.address, ...changes.address };
  }
  return updated;
}

A single spread handles { plates: 5 } correctly. It does not handle { address: { city: 'Mumbai' } } — that would replace the whole address, losing the pincode, and any address the copy keeps would still be shared with the original. The second spread rebuilds the address from the old one plus the changes. This is why deeply nested state is unpleasant to update by hand, and why libraries exist for it.

Practice

  1. Build an order object with a customer, a plate count, a rate and a nested address containing a city and a pincode. Print the city.
  2. Read a property with dot notation and the same one with brackets.
  3. Put a key in a variable and look it up with order[key]. Then do it wrong with order.key and explain the undefined.
  4. Add a 'delivery note' key with a space in it and read it back.
  5. Cause the deep-read error. Read order.discount (fine) and then order.discount.amount (not fine). Read the message and identify which part was undefined.
  6. Add a total() method and call it. Then rewrite it as an arrow and watch it break — this is module 3's rule, in the place it bites.
  7. Loop the object with Object.entries and print each key and value.
  8. Set both obj[1] and obj['1'] and confirm you end up with one property.
  9. Prove reference semantics. Assign an object to a second variable, change it through the second, and read it through the first. Then do the same with a number and note the difference.
  10. Harder. Write updateOrder(order, changes) returning a new order with the changes applied and the original untouched. Test it with a change to a top-level field, then with a change to something inside address, and work out why the second one needs more than a spread.

Next: arrays — the other way data clumps, and the methods you will use daily.

Stuck on this lesson?

Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.

About the internship