Topic 02

Objects

JavaScriptHands-on

An array is good at "many things, in order". But a single thing often has several parts: a project has a title, a description, some tags and an address. Four separate names for those four parts would have to be kept in step by hand, and a second project would need four more. An object groups values under names, so one value can describe one whole thing. A list of objects is the shape almost all real data takes, from a portfolio to a weather forecast.

Think of a paper form with labelled boxes. The label printed beside each box is the key, and what is written in the box is the value. Every form of the same kind has the same labels, with different writing in the boxes. Objects work the same way, except that you can add a new box to one form whenever you like.

Keys and Values

An object is written in curly braces. Inside, each entry is a key, a colon and a value, with commas between entries. A key is a name, and a value can be anything: a string, a number, a boolean, an array, even another object. Each key and its value together are called a property, the word Chapter 6 used for the named values you reach with a dot. Paste the project below into the Console.

Console
> const project = {
    title: "Budget Tracker",
    description: "Adds up a month of spending from a spreadsheet.",
    tags: ["javascript"],
    url: "https://example.com/budget"
  };
undefined
> project.title
'Budget Tracker'
> project.tags[0]
'javascript'

The object describes one project with four properties. To read one, write the object's name, a dot and the key: project.title gives Budget Tracker. The next line goes one step further: project.tags is an array, and index 0 of that array is javascript. Read it from left to right: the project, its tags, the first tag.

Dot or Brackets?

There is a second way to read a property: square brackets holding the key as a string. project["title"] gives exactly the same as project.title. The brackets earn their place when the key is not known until the program runs, because the string inside them can come from a name.

Console
> project["title"]
'Budget Tracker'
> const field = "description";
undefined
> project[field]
'Adds up a month of spending from a spreadsheet.'
> project.field
undefined

The name field holds the string description, and brackets use that value, so the Console gives the project's description. The dot does not work this way. Writing project.field looks for a key literally called field, finds none, and answers undefined. So: the dot when you know the key as you write the code, brackets when the key is held in a name.

Changing and Adding

To change a property, assign a new value to it, with the same single equals sign as a name. To add a property, assign to a key the object does not have yet, and it appears.

Console
> project.title = "Budget Tracker 2";
'Budget Tracker 2'
> project.featured = true;
true
> project.featured
true

The title is now Budget Tracker 2, and the object has a fifth property, featured, set to true. Here is the promise from Chapter 6 kept. project was made with const, and its contents changed anyway. const fixes the name: project will always point at this same object. It says nothing about what is inside. Trying to point the name at a different object, with project equals a new pair of braces, is what gives the type error about a constant.

A List of Objects

Real data is usually an array of objects: one object per thing, all with the same keys. Here are the three projects your site will show, as an array. Put it at the top of js/main.js, in place of the practice code from earlier topics.

js/main.js
const projects = [
  {
    title: "Budget Tracker",
    description: "Adds up a month of spending from a spreadsheet.",
    tags: ["javascript"],
    url: "https://example.com/budget"
  },
  {
    title: "Recipe Cards",
    description: "A responsive page of recipes that prints cleanly.",
    tags: ["html", "css"],
    url: "https://example.com/recipes"
  },
  {
    title: "Weather Now",
    description: "Shows today's forecast for any city.",
    tags: ["javascript", "api"],
    url: "https://example.com/weather"
  }
];

The array holds three objects, each with the same four keys. Below the array, add the lines in the next block. The first reads the second project's title: projects[1] is the second object, because counting starts at zero, and .title is its title, so the Console shows Recipe Cards. The loop then goes through every project and logs each title in turn: Budget Tracker, Recipe Cards, Weather Now.

js/main.js
console.log(projects[1].title);

for (const project of projects) {
  console.log(project.title);
}
Console output
Recipe Cards
Budget Tracker
Recipe Cards
Weather Now
The projects array: three objects, four keys each
array
projects
index 0
an object: Budget Tracker
keys
title, description, tags, url
index 1
an object: Recipe Cards
keys
title, description, tags, url
index 2
an object: Weather Now
keys
title, description, tags, url

What If a Key Is Missing?

Reading a key an object does not have is not an error: the answer is undefined. The error comes one step later, when you read a key of that undefined. Real data is full of gaps, so this matters. The projects have no key called links, and the Console below shows what happens when code expects one. Because projects was made at the top level of main.js, the Console can use it too.

Console
> projects[0].links
undefined
> projects[0].links.demo
Uncaught TypeError: Cannot read properties of undefined (reading 'demo')
> projects[0].links?.demo
undefined
> projects[0].links?.demo ?? "No demo"
'No demo'

The first line answers undefined: no links. The second tries to read demo from that undefined and stops with the type error from Chapter 6. The third line puts a question mark before the dot. That is called optional chaining, and it means: if the thing before me is undefined or null, stop and answer undefined instead of failing. The last line adds two question marks and a default. That is the nullish coalescing operator, and it means: if the left side is undefined or null, use the right side instead. So the answer is the text No demo.

You may see two vertical bars used for defaults in other code. They also replace zero and the empty string, which are falsy but can be real values, so this book uses the two question marks. In Chapter 11, some projects from a live service arrive with their description set to null, and this is exactly how the page will show "No description" instead.

Common Confusions
  • "An object made with const is frozen." The name is fixed; the contents are not. You can change the properties of a const object and add new ones.
  • "Reading a missing key is an error." It gives undefined. The error comes when you read a key of that undefined, and optional chaining guards against it.
  • "Objects and arrays are separate worlds." Arrays hold objects and objects hold arrays. Real data is almost always both at once, like the projects.
Why It Matters
  • The projects your site shows are exactly this shape: an array of objects with the same keys.
  • Optional chaining and a default prevent the most common crash with real data, a missing value read as if it were there.

Knowledge Check

Using the site's projects array, what is projects[2].tags[1]?

  • The string javascript, its first tag
  • The string api, from Weather Now
  • The string css, from Recipe Cards
  • undefined, as that tag does not exist

The name key holds the string "url". How do you read that property of project?

  • With a dot: project.key
  • With quotes: project["key"]
  • With brackets: project[key]
  • With a guard: project?.key

A project has no links key. What does project.links?.demo ?? "No demo" give?

  • The string No demo, from the default text
  • A TypeError about reading undefined
  • The value undefined, as links is missing
  • The value null, which marks a missing key

const project = { title: "A" }; is followed by project.title = "B";. What happens?

  • A TypeError, since project is a const
  • A second title is added beside the first
  • Nothing, the title silently stays A
  • The object's title is changed from A to B

You got correct