Working with Objects

Property access, computed keys, nested data, and reading values that might not be there.

An object is a bag of named values. A show is an object: it has a title, a year, a list of genres, and a rating.

Making an object

An object literal is a pair of braces with key: value pairs inside. A value can be anything, including another object.

const show = {
  title: "Severance",
  year: 2022,
  genres: ["Drama", "Thriller"],
  rating: { average: 8.7 },
};

console.log("title:", show.title);
console.log("year: ", show.year);
console.log("typo: ", show.titel);

show.title and show.year read real properties. show.titel is a typo, so it prints undefined.

A wrong property name is not an error. You find out later, when undefined turns up somewhere else.

Shorthand properties

When a variable already has the name you want for the key, write it once.

const title = "Severance";
const year = 2022;

// Shorthand: { title, year } means { title: title, year: year }.
const show = { title, year };

console.log("title:", show.title);
console.log("year: ", show.year);

const name = "Andor";
// The key is the variable's name, so there is no title here.
const other = { name };

console.log("other title:", other.title);

show has the two properties you expect. other has a name, not a title, so the last line prints undefined.

Dot access and bracket access

Dot notation needs a name you can type out when you write the code. Brackets take any expression, so they also work with a key held in a variable.

const show = { title: "Severance", premiered: "2022-02-18" };
const field = "premiered";

console.log("dot:      ", show.premiered);
console.log("brackets: ", show["premiered"]);
console.log("variable: ", show[field]);
console.log("dot again:", show.field);

The first three lines all print the same date. The last one prints undefined, because show.field looks for a property literally named field.

Computed keys

The same brackets work when you build an object, not only when you read one. Put the expression in brackets on the left of the colon.

const field = "network";
const value = "Apple TV+";

const show = {
  title: "Severance",
  [field]: value,
};

console.log("network:", show.network);

// Without the brackets, the key is the word "field".
const wrong = { title: "Severance", field: value };

console.log("wrong.network:", wrong.network);
console.log("wrong.field:  ", wrong.field);

[field]: value used the value of field as the key, so show.network works. In wrong the key really is field, so wrong.network is undefined.

This is how one function can set whichever field a form changed.

Nested objects

Real show data nests. You walk down it one property at a time.

const show = {
  title: "Severance",
  rating: { average: 8.7 },
  network: { name: "Apple TV+", country: "US" },
};

console.log("average:", show.rating.average);
console.log("channel:", show.network.name);
console.log("one step short:", show.rating);

The last line prints { average: 8.7 }. Stopping one step short gives you the inner object, not the number inside it.

Reading what might not be there

A path only works while every step exists. Reading a property off undefined throws an error.

const show = { title: "Andor", rating: { average: 8.4 } };

try {
  console.log("no ?.:", show.network.name);
} catch (error) {
  console.log("no ?.:", error.constructor.name + ":", error.message);
}

// ?. stops at the first missing step instead of throwing.
console.log("with ?.:  ", show.network?.name);
console.log("fallback: ", show.network?.name ?? "Unknown network");

This show has no network, so show.network.name throws a TypeError. show.network?.name gives undefined instead. That is optional chaining.

undefined is not something to show a reader, so ?? supplies a name to display. let, const, and Scope covers why ?? and || disagree about what counts as missing.

Common mistakes

  • Using dot notation with a variable key. show.field looks for a property called field; you want show[field].
  • Reaching through a missing level. show.network.name throws when network is missing, so write show.network?.name.
  • Trusting a typo to fail loudly. A misspelled property is undefined, and the problem shows up further down.

Exercise

read should return the value stored under key, and fall back to "not set" only when the show has no such property. It prints not set four times instead.

The output should read:

title:   Severance
rating:  0
network: 
missing: not set

The network line ends after the label, because an empty string is a real value. Two things are wrong: how read looks up the key, and which fallback operator it uses.

const show = { title: "Severance", rating: 0, network: "" };

function read(key) {
  return show.key || "not set";
}

console.log("title:  ", read("title"));
console.log("rating: ", read("rating"));
console.log("network:", read("network"));
console.log("missing:", read("missing"));