Skip to content

Objects and arrays

An app's data almost always consists of objects and arrays. A thing in the catalog is an object, and the catalog is an array of such objects. Vue later makes this data reactive, but the basics stay the same.

Learning goals

After this unit you can

  • create objects and arrays and read and change their values,
  • explain why two variables can point to the same object,
  • copy objects and arrays with spread,
  • pull values out with destructuring and catch missing values with ?..

An object groups values under names, its keys. You read a value with a dot or with square brackets. You need the square brackets when the key is stored in a variable or isn't a valid name.

item.mjs
const item = {
id: 3,
name: 'Raclette oven',
deposit: 30,
tags: ['kitchen', 'winter'],
}
console.log(item.name, item['deposit'], item.tags[1])
item.available = true
console.log(Object.keys(item).join(', '))
console.log(item.owner?.name ?? 'association')

An object can be extended after it was created. item.available = true adds a new key, even though item was declared with const. Object.keys returns all keys as an array, Object.values the values and Object.entries both as pairs.

The last line asks for the name of the owner, which doesn't exist. Without protection, item.owner.name would throw a TypeError, because item.owner is undefined. Optional chaining with ?. stops in this case and returns undefined, and ?? puts "association" in its place.

An array is an ordered list. Positions are counted from 0, and length says how many elements it has.

list.mjs
const names = ['Drill', 'Tent']
names.push('Ladder')
console.log(names.length, names.includes('Tent'), names.indexOf('Ladder'))
const [first, ...rest] = names
console.log(first, rest)
const { name, deposit = 20 } = { name: 'Jigsaw' }
console.log(name, deposit)

push appends an element at the end and changes the array itself. includes checks whether a value occurs, and indexOf returns its position or -1 if it is missing. The second and third lines of output show destructuring, which comes further down.

A variable doesn't hold the object itself, but a reference to it. If you assign one variable to another, both point to the same object.

references.mjs
const tent = { name: 'Tent', available: true }
const sameTent = tent
sameTent.available = false
console.log(tent.available)
const copy = { ...tent, available: true }
console.log(tent.available, copy.available)
console.log({ deposit: 20 } === { deposit: 20 }, tent === sameTent)
const names = ['Tent']
const more = [...names, 'Projector']
console.log(names.length, more.length)

sameTent isn't a second tent, but a second name for the same one. That's why tent.available is false as well after the change through sameTent. A copy is made with the spread operator ..., which carries all fields over into a new object. Fields that come after it overwrite the copied ones, so copy.available is true again. Spread works the same with arrays, and [...names, 'Projector'] is a new array with one more element.

For objects, === compares the references, not the content. Two objects with the same content are therefore not equal, but an object is equal to itself.

Spread only copies the top level. If an object contains another object or an array, the original and the copy share that inner object. The unit JSON and data formats shows how to copy inner objects as well with structuredClone. Part 1 covers this in more detail in the chapter "Values and references".

Destructuring pulls several values out of an array or an object at once. For arrays, position counts. const [first, ...rest] = names takes the first element and collects the rest in a new array. For objects, the name counts. const { name, deposit = 20 } = item takes two fields and sets a default in case deposit is missing. In the example above the jigsaw has no deposit, so it is 20.

Destructuring also works for parameters, and function describe({ name, deposit }) takes the fields straight from the object passed in. In Vue you meet it with composables that return several values in one object, such as const { favorites, toggle } = useFavorites().

MistakeWhat helps
A copy made with const copy = original changes the original too.Copy with { ...original } or [...original], and nested data with structuredClone.
item.owner.name throws a TypeError because owner is missing.Write item.owner?.name.
Two objects with the same content are compared with ===.Compare a unique field such as the id.
names[names.length] is meant to return the last element and is undefined.Use names[names.length - 1] or names.at(-1).
const is meant to protect an object from changes.const only prevents reassigning the variable, and the fields can still change.

Exercise Getting started

Create an object member with the fields id ('M-117'), firstName ('Martina') and memberSince (2024). Use it to print the line "Martina (M-117) since 2024".

Solution
solution-1.mjs
const member = {
id: 'M-117',
firstName: 'Martina',
memberSince: 2024,
}
console.log(`${member.firstName} (${member.id}) since ${member.memberSince}`)

A template literal inserts the three values into the text. The unit Strings and template literals has more on template literals.

Exercise Going further

Create an empty basket basket and add "Tent" and "Sleeping bag". Then add "Tent" a second time, but only if it isn't in the basket yet. At the end the program should print 2 Tent + Sleeping bag.

Solution
solution-2.mjs
const basket = []
basket.push('Tent', 'Sleeping bag')
if (!basket.includes('Tent')) basket.push('Tent')
console.log(basket.length, basket.join(' + '))

includes prevents the second tent, and join(' + ') joins the names into one string. basket is declared with const and can still be filled, because push changes the array, not the variable.

Exercise Challenge

You have const reservation = { item: { name: 'Projector' }, from: '2026-11-07' }. With a single destructuring, get the thing's name as name, the end as to with the start as its default, and the note as note. Then print the member's e-mail address or "no e-mail" without the program crashing.

Solution
solution-3.mjs
const reservation = { item: { name: 'Projector' }, from: '2026-11-07' }
const { item: { name }, to = reservation.from, note } = reservation
console.log(name, to, note)
console.log(reservation.member?.email ?? 'no e-mail')

The output is Projector 2026-11-07 undefined and then no e-mail. item: { name } steps into the inner object, and item itself doesn't become a variable. to = reservation.from applies because there is no to, and note stays undefined. reservation.member is missing, so ?. stops before .email is read, and ?? puts in the text.

Text and figures are licensed under CC BY-SA 4.0, the code under the MIT License and the course data under CC0 1.0. The figures use the font Kalam under the SIL Open Font License 1.1. The sources are on GitHub.