Object Basics

Mental model: An object is a bag of named references, and the name can be computed while the program runs.

Level: beginner · about 11 minutes

const track = {
  title: 'Clair de Lune',
  minutes: 5,
  'composer name': 'Debussy',   // a key with a space needs quotes
};

console.log(track.title);            // 'Clair de Lune'
console.log(track['composer name']); // 'Debussy'

Everything in this lesson is a variation on these six lines.

An object maps string (or symbol) keys to values. The keys are always strings under the hood: track[5] and track["5"] reach the same property, because the number is converted first.

Dot versus bracket

Dot: the key is fixed

track.minutes      // 5
track.minutes = 6  // writes

// You are typing the literal
// name of the property.

Bracket: the key is an expression

const field = 'minutes';
track[field]       // 5

// Works for spaces, digits,
// and anything computed.

Use dot when you know the name as you type it. Use brackets when the name lives in a variable, comes from user input, or is not a valid identifier.

Computed keys in the literal

const key = 'status';
const code = 404;

const response = {
  [key]: 'error',            // computed key
  [`http_${code}`]: true,    // built from a template literal
};

console.log(response);       // { status: 'error', http_404: true }

Nesting is just objects inside objects

const user = {
  name: 'Ada',
  address: { city: 'London', postcode: 'N1' },
  tags: ['maths', 'engines'],
};

console.log(user.address.city);  // 'London'
console.log(user.tags[1]);       // 'engines'
console.log(user.pet?.name);     // undefined, no crash (lesson 7.8)

Adding and removing

const config = { theme: 'dark' };

config.locale = 'en-GB';        // add
config.theme = 'light';         // update
delete config.locale;           // remove the property entirely

console.log(config);            // { theme: 'light' }
console.log('locale' in config); // false

Key order is defined, and it is not insertion order

const mixed = { b: 1, 2: 'two', a: 3, 1: 'one' };

console.log(Object.keys(mixed)); // ['1', '2', 'b', 'a']

Integer-like keys come first, in ascending numeric order.

  1. Integer-like keys ("0", "1", "42") in ascending numeric order.
  2. All other string keys in insertion order.
  3. Symbol keys last, in insertion order.

Shorthand, for properties and methods

const title = 'Objects';
const lessons = 8;

const module7 = {
  title,                            // same as title: title
  lessons,
  describe() {                      // method shorthand
    return `${this.title}: ${this.lessons} lessons`;
  },
};

console.log(module7.describe());    // 'Objects: 8 lessons'
`obj.a`
fixed name, shortest to read
`obj["a b"]`
any string, including invalid identifiers
`obj[k]`
name from a variable
`{ [k]: v }`
computed key while building the object
`delete obj.a`
removes the key, not just the value
const field = 'size';
const box = { size: 10 };
console.log(box.field, box[field]);

box.field looks for a property literally named field, which does not exist, so you get undefined. box[field] evaluates the variable first, finds "size", and returns 10.

Try it yourself

Shape a record

const field = 'level';

const lesson = {
  id: '7.1',
  title: 'Object Basics',
  [field]: 'beginner',
  minutes: 11,
};

console.log(lesson[field]);        // 'beginner'
console.log(Object.keys(lesson));  // insertion order, no integer keys here

delete lesson.minutes;
console.log(lesson);

Add a nested meta object. Then read one of its fields using a key held in a variable.

Exercises

Tally the words

Write tally(words) which returns an object mapping each word to how many times it appears. It must work for every word, including awkward ones like "constructor" and "toString", so build a container with no inherited properties.

Check yourself

What does this log?
0,1,x — Integer-like keys are visited first in ascending numeric order ("0", then "1"), and other string keys follow in insertion order. Insertion order applies only to the non-integer keys.
When must you use bracket access instead of dot access?
When the key is not a valid identifier or is held in a variable — Dot access needs the literal property name typed in the source. Brackets take any expression, which is what you need for "composer name", "404", or a key stored in a variable.
What is the difference between delete obj.a and obj.a = undefined?
delete removes the key; assigning undefined keeps the key with an empty value — After assignment the key still shows up in Object.keys, in and JSON.stringify handling. After delete the property is gone from the object entirely.

Common mistakes

  • Writing obj.key when the name lives in a variable named key. You get undefined, not an error.
  • Assuming object keys keep insertion order. Integer-like keys jump to the front, sorted.
  • Treating obj.a = undefined as removal. The key stays until you delete it.

Takeaways

  • Property keys are strings or symbols. Numbers are converted to strings.
  • Dot access needs a literal name, bracket access takes any expression.
  • Computed keys { [expr]: value } let you build shapes at runtime.
  • Integer-like keys are listed first in ascending order, then other strings in insertion order.