Objects and JSON

Learn to model real data with JavaScript objects: create and change properties, loop over them, and see why copying a variable does not copy the object it refers to. Give objects behavior with methods and work out what the this keyword refers to in each kind of call. Unpack and combine data with destructuring, spread and rest, store collections in Map and Set, and convert data to and from JSON text with JSON.stringify and JSON.parse.

BeginnerLesson 4 of 9 in JavaScript Programming Course

What you will learn

  1. Describe real things in code as objects with named properties, read and change those properties, and understand how objects are stored, copied and compared.
    • Create objects and work with their properties
      Write object literals, read properties with dot and bracket notation, and add, change and delete properties.
    • Check and loop over an object's properties
      Test whether a property exists, list keys, values and entries, and loop over or transform an object's properties.
    • Understand references, copying and comparison
      Explain why objects are shared by reference, compare objects correctly, and make shallow and deep copies.
    • Work with nested data
      Read and process objects inside objects and arrays of objects, guard against missing parts with ?. and ??, and choose a structure that fits the data.
  2. Write methods that use their object's data, explain how the this keyword gets its value in each kind of call, and keep the right this when a method is passed around.
    • Write methods that use their object's data
      Define methods with the shorthand syntax, use this to read and update the object, and call methods correctly.
    • Explain how this gets its value
      Work out the value of this from the way a function is called, and explain why arrow functions behave differently.
    • Keep this when passing methods as callbacks
      Recognize when a method loses its object, and fix it with an arrow wrapper, bind, call or apply.
  3. Pull values out of objects and arrays with destructuring, and copy, merge and collect values with the spread and rest syntax.
    • Destructure objects
      Unpack object properties into variables, with renaming, default values, nested patterns and destructured function parameters.
    • Destructure arrays and other iterables
      Unpack values by position, skip and default them, swap variables, and destructure entries in loops.
    • Copy, merge and collect with spread and rest
      Use spread to copy and merge objects and arrays and to pass array items as arguments, use rest to collect values, and tell the two apart.
  4. Use Map for key-value collections with keys of any type and Set for collections of unique values, and choose between them and plain objects and arrays.
    • Store key-value pairs in a Map
      Create and update a Map, iterate over its entries, convert between Maps and objects, and decide when a Map fits better than an object.
    • Keep unique values in a Set
      Create and update a Set, remove duplicates from arrays, and compare collections with set operations.
  5. Read and write the JSON text format, turn JavaScript values into JSON with JSON.stringify and JSON text back into values with JSON.parse, and handle the cases where the two do not match.
    • Read and write the JSON format
      Recognize valid JSON, explain how it differs from a JavaScript object literal, and spot common syntax errors.
    • Turn values into JSON text with JSON.stringify
      Convert objects and arrays into JSON strings, format the output, choose which properties to include, and predict what gets dropped or changed.
    • Turn JSON text into values with JSON.parse
      Parse JSON safely, restore values such as dates with a reviver, check the parsed data, and round-trip data through JSON.

Objects and JSON study guide

5 chapters

1

Model data with objects

An object keeps related values together under names, so a record can describe one book, person, or reading. Working with those names lets you change one field, examine a whole record, and organize lists of records. You will also trace which variables point to the same object. That distinction matters whenever a change appears in two places, a copy contains nested data, or a missing field needs a sensible default.

1.1 Create objects and work with their properties

An object groups related values under names. Each property pairs a key with a value. Use one object for a record and an array of objects for an ordered list. Parallel arrays can lose their alignment when only one is sorted or changed.

Create and read a record
const book = { title: "Dune", pages: 412, available: true };
book.title;    // "Dune"
book["title"]; // "Dune"

Property values may be arrays, objects, or functions as well as primitive values. Keys are strings or symbols; these examples use strings. String keys that are valid identifiers need no quotes. Keys containing spaces or hyphens need quotes: { "release-year": 1965 }. Numeric keys become strings, so { 1: "a" } has key "1". Object literals permit a trailing comma.

Dot access uses the literal name after the dot. Bracket access evaluates an expression:

const field = "pages";
book[field]; // 412
book.field;  // undefined

The second expression looks for a property named "field". Brackets also handle book["release-year"]. A missing property returns undefined; continuing through it, as in book.author.name, throws a TypeError when author is missing.

Change the object's contents
book.author = "Frank Herbert";
book.pages = 896;
delete book.available;

Assignment adds a missing property or replaces an existing value. Deletion removes the property itself, so "available" in book is now false. const keeps the variable attached to this object but permits changes to its contents.

For const reading = { city: "Nairobi", tempC: 24.5 };, reading.tempC += 1.5; stores 26. Then reading.humidity = 61; adds a field. With const key = "humidity";, reading[key] reads 61. A name such as tempC makes the measurement's unit visible.

Use variables in a literal
const name = "Lena";
const age = 29;
const person = { name, age };
const lang = "nl";
const greeting = { [lang]: "Hallo" };

Shorthand { name, age } means { name: name, age: age }. A computed name evaluates the expression inside square brackets: greeting has property "nl". Without those brackets, { lang: "Hallo" } would create the literal key "lang".

Key Takeaways

  • An object groups related values into named properties.
  • Use brackets for a key stored in a variable or a key containing a hyphen.
  • Assignment adds or replaces a property; delete removes it.
  • const fixes the variable, not the object contents.
  • Shorthand takes a variable value; a computed key evaluates an expression.

1.2 Check and loop over an object's properties

Checking whether a field exists is different from checking its value. An empty string, zero, or false may be a perfectly valid stored value.

Ask the right existence question
const item = { stock: 0, note: undefined };
Object.hasOwn(item, "stock"); // true
"note" in item;                // true
Boolean(item.stock);          // false

Object.hasOwn(obj, key) checks only the object's own properties. The in operator also checks inherited properties: "toString" in {} is true, while Object.hasOwn({}, "toString") is false. Older code often calls obj.hasOwnProperty(key).

A condition such as if (item.stock) tests truthiness. It cannot distinguish a missing field from a present field holding zero. The same issue applies to "", false, null, undefined, and NaN. Even a value of undefined can belong to an existing property, as note does above.

Choose keys, values, or pairs

For const scores = { Ana: 82, Ben: 74, Chen: 91 };, these functions return arrays of own enumerable string-keyed properties:

ExpressionResult
Object.keys(scores)["Ana", "Ben", "Chen"]
Object.values(scores)[82, 74, 91]
Object.entries(scores)[["Ana", 82], ["Ben", 74], ["Chen", 91]]

The object has no array-style length: scores.length is undefined. Object.keys(scores).length counts three properties. To total the scores, reduce the values with an initial zero. Their sum is 247 and their mean is about 82.33.

Loop with both parts available
let bestName;
let bestScore = -Infinity;
for (const [name, score] of Object.entries(scores)) {
  if (score > bestScore) {
    bestName = name;
    bestScore = score;
  }
}

The loop compares 82, 74, then 91 with the best score seen so far. It finishes with "Chen" and 91. The square-bracket pattern gives names to each pair's two parts.

A plain object is not iterable, so for...of scores throws a TypeError. Object.entries(scores) supplies an iterable array. The older for...in loop produces string keys and can include inherited enumerable properties.

Transform entries back into an object
const weightsG = { flour: 500, sugar: 250, salt: 5 };
const weightsKg = Object.fromEntries(
  Object.entries(weightsG).map(([item, g]) => [item, g / 1000])
);

entries produces pairs, map changes each value while keeping its key, and fromEntries rebuilds the record. The result is { flour: 0.5, sugar: 0.25, salt: 0.005 }; weightsG stays unchanged.

Own property order is defined. Array-index keys from "0" through "4294967294" appear first in ascending order; other string keys follow insertion order. A leading zero, as in "01", prevents a key from being an array index. Thus Object.keys({ b: 1, 2: "x", a: 2, 1: "y" }) gives ["1", "2", "b", "a"]. Use an array when positions carry meaning.

Key Takeaways

  • Property existence and truthiness answer different questions.
  • Object.hasOwn checks own properties; in also checks inherited ones.
  • Object.entries provides key-value pairs for loops and transformations.
  • Object.fromEntries rebuilds an object from pairs.
  • Array-index keys come before other string keys in property order.
  • Count own enumerable string keys with Object.keys(obj).length.

1.3 Understand references, copying and comparison

Two variables can refer to one object. Before predicting an update, decide whether the code created a new object or copied a reference to an existing one.

Trace identity separately from data
const p1 = { x: 1 };
const p2 = p1;
p2.x = 99;
p1.x;       // 99
p1 === p2;  // true

Assignment copied the reference. Both names reach the same property. Primitive assignment works differently: after let a = 5; let b = a; b = 6;, a remains 5.

Objects compare by identity with both === and ==. Two literals create separate objects even when all fields match: ({ x: 1 } === { x: 1 }) is false. To compare their data, compare the relevant properties. A matching printed appearance is not an identity check.

Passing an object into a function also copies its reference. A function that runs order.status = "shipped"; changes the caller's object. Returning { ...order, status: "shipped" } instead creates a new top-level record.

Separate const, freezing, and copying

const cart = { items: 2 }; allows cart.items = 3; but reassigning cart = {}; throws a TypeError. Object.freeze(cart) blocks changes to its own properties. In strict mode, attempts to add, remove, or overwrite them throw a TypeError.

Freezing is shallow. If a frozen record holds another object, that nested object's fields can still change. A frozen container and a deeply unchangeable structure are different things.

Follow a shallow copy
const settings = { theme: "dark", font: { size: 14 } };
const copy = { ...settings };
copy.theme = "light";
copy.font.size = 18;

Spread creates a new outer object. Its theme value is copied, so changing copy.theme leaves settings.theme as "dark". Its font value is a reference, copied into both records. Updating copy.font.size therefore makes settings.font.size equal 18 too.

Object.assign({}, settings) also makes a shallow copy. A shallow copy separates the outer record while retaining the identity of nested objects and arrays.

Copy nested data when it must be independent
const deep = structuredClone(settings);
deep.font.size = 22;
settings.font.size; // 18

structuredClone copies nested objects and arrays, along with supported values such as Map, Set, and Date. It also handles circular references. The new nested font can change independently. Functions cannot be cloned this way: an object containing a method causes a DataCloneError.

A JSON round trip, JSON.parse(JSON.stringify(obj)), copies only JSON-compatible data. It drops object properties holding undefined or functions, changes dates into strings, and loses Map and Set entries unless you convert them. Choose a clone for independent supported JavaScript data, and choose JSON when you intend to create a text representation.

Key Takeaways

  • Assignment and function calls copy object references.
  • Objects compare by identity, even when their fields match.
  • A shallow copy shares nested objects.
  • Object.freeze blocks own-property changes but is shallow.
  • structuredClone copies supported nested data and rejects functions.

1.4 Work with nested data

Nested data combines records and lists. Read a path one step at a time, checking whether each step reaches an object, an array, or a final value.

Read and process a list inside a record
const order = {
  id: 1042,
  customer: { name: "Sara", city: "Lisbon" },
  items: [
    { product: "Tea", qty: 2, unitPrice: 4.5 },
    { product: "Mug", qty: 1, unitPrice: 9 }
  ]
};

order.customer.city reads "Lisbon". order.items[1].product first selects the items array, then its second record, then "Mug". order.items.length is 2.

The array methods you already know work with record properties. map(item => item.product) produces product names. filter(item => item.qty > 1) keeps the tea line. find(item => item.product === "Mug") returns the existing mug record, so changing that returned record changes the item in the order.

To calculate the total, use order.items.reduce((sum, item) => sum + item.qty * item.unitPrice, 0). The first line adds 9; the second adds 9 more, producing 18. Computing a total when needed prevents a separately stored total from becoming stale when items change.

Read optional data with a fallback
order.shipping?.date;                 // undefined
order.customer?.city ?? "unknown";    // "Lisbon"

Optional chaining, ?., stops when the value immediately to its left is null or undefined. A direct order.shipping.date would throw a TypeError. Use a check at each optional link in a longer path. Bracket access and calls also support it: order.items?.[0] and order.print?.().

Optional chaining is for values allowed to be missing. Required fields still need checks or errors that reveal a broken record. It cannot appear on the left of assignment: order.shipping?.date = "2026-05-01" is a syntax error.

The nullish coalescing operator, ??, uses its right side only for null or undefined. It preserves zero, false, and "". For const qty = 0;, qty ?? 1 remains zero, while qty || 1 becomes 1 because || replaces every falsy value.

Pick a useful shape

An array of records suits listing, filtering, and ordering. An object keyed by ID supports direct lookup: usersById["u23"].name. A Map is useful when lookup keys are frequently added and removed.

Newer JavaScript offers Object.groupBy, widely available since 2024:

const tasks = [
  { title: "Book flights", status: "done" },
  { title: "Pack", status: "todo" },
  { title: "Renew passport", status: "done" }
];
const grouped = Object.groupBy(tasks, task => task.status);

grouped.done holds two records and grouped.todo holds one. The result has no prototype, so it has no inherited toString method. An older alternative builds the arrays explicitly for these status keys:

const groupedOlder = tasks.reduce((groups, task) => {
  if (!Object.hasOwn(groups, task.status)) {
    groups[task.status] = [];
  }
  groups[task.status].push(task);
  return groups;
}, {});

The first record in each group creates its array. Later records append to that same array. This accumulator is an ordinary object; the newer method's result has no prototype. Use consistent names and value types across records; names such as distanceKm and tempC also keep units visible.

Key Takeaways

  • Read a nested path from its outer object toward the needed value.
  • find returns the existing record, not a copy.
  • ?. stops at a nullish link; ?? supplies a nullish fallback.
  • Use an array for an ordered list and keyed data for direct lookup.
  • Object.groupBy groups records into arrays under keys.
  • Consistent field names, types, and units make records easier to process.
2

Give objects behavior with methods and this

Methods let an object combine its data with actions that use that data. The small word this connects an action to an object, but its value depends on how the function runs. You will follow ordinary method calls, borrowed methods, and callbacks, then choose a wrapper or a bound function when an action must keep its object. The examples run as modules, so strict mode applies unless an example explicitly uses non-strict code.

2.1 Write methods that use their object's data

A method is a function-valued property called through an object. It lets an action use the record's current data.

Write a method with this
const counter = {
  count: 0,
  increment() {
    this.count += 1;
    return this.count;
  }
};
counter.increment(); // 1
counter.increment(); // 2

The shorthand increment() { ... } defines a regular function-valued property. In counter.increment(), the receiver counter becomes this. Each call reads the stored count, adds one, and returns the new number.

A bare count selects a variable, not the property. Without a matching variable, count += 1 throws ReferenceError. Use this.count to reach the receiver's field.

Read a function or run it

counter.increment reads a value of type "function". Parentheses in counter.increment() run it and give its return value. When passing an action for later use, the receiving code needs the function, not the result of executing it now.

Built-in methods use this calling shape too: Math.max(3, 7) returns 7 and "abc".toUpperCase() returns "ABC".

Reuse an action
function describe() {
  return this.name + " (" + this.year + ")";
}
const film = { name: "Up", year: 2009, describe };
const song = { name: "Hello", year: 2015, describe };

film.describe() returns "Up (2009)"; song.describe() returns "Hello (2015)". Using this lets the same function read either receiver. Hard-coding film.name would tie it to the film variable.

Return the receiver for another action
const playlist = {
  tracks: [],
  add(title) { this.tracks.push(title); return this; },
  count() { return this.tracks.length; }
};
playlist.add("Rain").add("Waves").count(); // 2

Each add returns the playlist, allowing the next method lookup on that object. Returning a number from add would break the chain because the next add would be looked up on that number.

Ordinary JSON conversion omits methods: stringifying { name: "Kit", hi() { return 1; } } writes only name. Parsing cannot restore omitted code. Records for saving or exchange often keep plain data and use separate processing functions.

Key Takeaways

  • A method is a function-valued property called through an object.
  • In obj.method(), a regular method uses obj as this.
  • A function value and the result of calling it are different values.
  • Returning this can let another method call follow.
  • Ordinary JSON conversion omits function-valued properties.

2.2 Explain how this gets its value

For a regular function, the call determines this. Storing a function in an object does not permanently attach it to that object.

Follow the receiver at the call
const a = { n: "A", who() { return this.n; } };
const b = { n: "B" };
b.who = a.who;
a.who(); // "A"
b.who(); // "B"

Both properties hold the same function. The receiver before the dot differs, so the results differ. The function's original location does not decide which object it reads.

For a plain call, f(), strict mode supplies undefined as this. JavaScript modules always run in strict mode; a script or function can also begin with "use strict";. A non-strict regular function called plainly uses the global object, globalThis, instead. In a browser that global is window.

Recognize an arrow's surrounding context

An arrow has no this of its own. It keeps the this from the surrounding code where it was created. An object literal does not create a new this context.

// At the top level of a module:
const user = {
  name: "Ida",
  hi: () => "Hi, " + this.name
};

Calling user.hi() throws a TypeError: the arrow captured the module's top-level this, which is undefined. Changing the call to look like a method cannot give the arrow a new receiver. At the top level of a classic script, the surrounding this is the global object instead.

Use a regular method when the action needs the calling object. Use an arrow inside that method when a callback needs to keep the method's context:

const group = {
  title: "Team",
  members: ["Ana", "Ben"],
  list() {
    return this.members.map(m => this.title + ": " + m);
  }
};
group.list(); // ["Team: Ana", "Team: Ben"]

list gets group from its method call. The arrow is created inside that running method and keeps the same this. A strict regular callback without a supplied receiver would receive undefined, so this.title would fail there.

Use a decision order

When you encounter a call, first identify the function being called:

  1. An arrow keeps its surrounding this; call, apply, and bind cannot replace it.
  2. A bound regular function keeps its bound this, even through call or apply.
  3. An unbound regular function called with call or apply uses the supplied this.
  4. An ordinary obj.method() call uses obj.
  5. A plain regular call uses undefined in strict mode or the global object in non-strict mode.

Callbacks can have a receiver supplied by their caller. For example, a regular DOM event listener registered with addEventListener receives the element it is registered on as this. Read how the receiving code invokes a callback before assuming it is a plain call.

Key Takeaways

  • A regular function gets this from its call, so borrowing a method changes its receiver.
  • A strict plain call uses undefined as this.
  • An arrow keeps the surrounding this where it was created.
  • A bound receiver takes precedence over later call or apply.
  • An arrow inside a regular method can keep that method's this.

2.3 Keep this when passing methods as callbacks

Taking a method out of its object preserves the function but loses the original calling expression. A callback must be called in a way that supplies the object it needs.

Diagnose a detached method
const counter = {
  count: 0,
  increment() {
    this.count += 1;
    return this.count;
  }
};
const inc = counter.increment;
inc(); // TypeError in strict mode

inc() is a plain call, so this is undefined. Reading this.count fails before an increment can happen. In non-strict code, a plain call uses the global object and may write a global count instead; it does not reach the counter.

The same issue appears in array callbacks. [1, 2].map(counter.increment) supplies a function to map without its original receiver. The array values passed as arguments do not supply this.

Wrap the method call
const results = [1, 2].map(() => counter.increment());
// results is [1, 2]; counter.count is 2

The callback is an arrow that makes a normal counter.increment() call. That call has a receiver. The wrapper deliberately ignores the values passed by map; each invocation simply increments once. This form is convenient when only one use needs the callback.

Bind a reusable function

Starting with a fresh counter at zero:

const boundInc = counter.increment.bind(counter);
const results = [1, 2].map(boundInc);
// results is [1, 2]; counter.count is 2

bind returns a new function; it does not call the method yet. The new function keeps counter as its receiver. Binding it again with a different object cannot replace that receiver. Each call to bind creates a distinct function, so save the result when code must recognize the exact same callback later.

Binding can also fix leading arguments. function multiply(a, b) { return a * b; } followed by const double = multiply.bind(null, 2); makes double(21) return 42. null fills the receiver position because this function does not use this; 2 fills its first ordinary parameter.

Run once with call or apply
function describe(unit) {
  return this.value + " " + unit;
}
describe.call({ value: 12 }, "kg");    // "12 kg"
describe.apply({ value: 12 }, ["kg"]); // "12 kg"

Both forms execute immediately with the chosen receiver. call supplies arguments separately; apply supplies them in an array. An arrow's context is fixed by its surroundings, so these methods cannot replace its this.

Passing counter.increment() as a callback runs the method immediately and passes its returned number. That is not the function map needs. In an event registration such as button.addEventListener("click", counter.increment()), the counter changes immediately and the numeric listener causes a TypeError. Pass a function value, an arrow wrapper, or a stored bound function.

Key Takeaways

  • Detaching a method removes its original receiver.
  • An arrow wrapper can make the required method call later.
  • bind creates a function with a fixed receiver and optional leading arguments.
  • call and apply run immediately; their argument formats differ.
  • Callback arguments must be functions, not the numeric result of running a method.
3

Unpack and combine data with destructuring, spread and rest

Destructuring gives names to selected pieces of an object or an ordered collection. Spread builds values from existing parts, while rest gathers the pieces left over. These forms often appear together in functions that receive options or return updated records. Reading their position carefully lets you predict which variables exist, which defaults apply, and whether nested data is shared with the original.

3.1 Destructure objects

Object destructuring reads named fields into local variables. It selects values without copying the whole source record.

Match and rename
const book = { title: "Dune", pages: 412 };
const { pages, title: bookTitle, rating: stars = 0 } = book;

Matching uses property names, not their order. This declares pages as 412, bookTitle as "Dune", and stars as 0. It declares no title or rating variable: the name after a colon is the local target.

A missing field supplies undefined. A default replaces only undefined, preserving null, zero, false, and empty strings. const { a = 5 } = { a: null }; therefore leaves a as null.

Follow nested fields
const data = {
  location: { name: "Oslo" },
  current: { tempC: -3, windKph: 18 }
};
const {
  location: { name },
  current: { tempC, windKph: wind }
} = data;

The declared variables are name, tempC, and wind, holding "Oslo", -3, and 18. The enclosing location and current names select paths; they do not also become variables.

A missing outer object makes a nested read fail. const { shipping: { date } } = {}; throws TypeError. Supply an outer default when that field may be undefined:

const { shipping: { date } = {} } = {};
// date is undefined

This does not replace shipping when its value is null. Destructuring null or undefined directly also throws, so the source must be usable before unpacking begins.

Receive named options
function formatUser({ name, age = "unknown" }) {
  return name + ", " + age;
}

formatUser({ name: "Mo" }) returns "Mo, unknown". Reordering the fields in { age: 40, name: "Ines" } still returns "Ines, 40". Calling formatUser with no argument fails because there is no object to unpack. A whole-parameter default permits that call:

function greet({ name = "guest" } = {}) {
  return "Hi, " + name;
}
greet(); // "Hi, guest"

The outer default handles an omitted argument; the inner default handles a missing name. Passing null still fails. To assign existing variables, wrap an object-pattern statement in parentheses: let x, y; ({ x, y } = { x: 4, y: 7 });. Otherwise the initial braces are read as a statement block.

Key Takeaways

  • Object patterns match properties by name.
  • A colon renames the variable; it does not change the source key.
  • Defaults replace undefined, including missing properties, and preserve null.
  • Nested patterns need a usable object at every level.
  • A whole-parameter default and a property default handle different missing values.
  • Parentheses let an object pattern assign to existing variables.

3.2 Destructure arrays and other iterables

Array destructuring reads values by position from an iterable. Commas control which values are selected; the variable names do not choose positions.

Select or skip positions
const medals = ["gold", "silver", "bronze"];
const [first, second] = medals;
const [, , third] = medals;

The variables receive "gold", "silver", and "bronze". Empty slots consume values without declaring variables. A name beyond the available values receives undefined. Defaults replace only undefined:

const [p, q = 10] = [1]; // p is 1; q is 10
const [r = 10] = [null]; // r is null
Gather the remaining values
const [head, ...tail] = [1, 2, 3, 4];
// head is 1; tail is [2, 3, 4]

The final rest element makes a new array, empty when nothing remains. It must be last. Any object values inside that array keep their identities; collecting them does not deeply clone them.

Read a returned pair or swap variables
function minMax(nums) {
  return [Math.min(...nums), Math.max(...nums)];
}
const [lo, hi] = minMax([4, 9, 1]);
// lo is 1; hi is 9

The caller follows the order agreed by the returning function. For many result fields, named object properties avoid having to remember positions.

With let x = 1, y = 2;, [x, y] = [y, x]; swaps the variables. The right-hand array is evaluated as [2, 1] before either assignment, preserving both old values without a temporary variable.

Unpack other iterables

Strings and Sets can be unpacked too: const [initial] = "Zoe"; gives "Z". Sets yield insertion-order values. Map entries are ordered pairs:

const distances = new Map([["park", 2], ["lake", 5]]);
for (const [place, distanceKm] of distances) {
  console.log(place, distanceKm);
}

The loop receives a pair, then the pattern separates its key and value. Object.entries supplies the same pair shape.

A plain object is not iterable, so const [name] = { name: "Ana" }; throws TypeError. Use curly braces to select its named field: const { name } = { name: "Ana" };. Square brackets require a sequence of values; braces select properties by name.

Key Takeaways

  • Array patterns match positions, and empty slots skip values.
  • A missing position becomes undefined unless a default replaces it.
  • A final rest element collects remaining values into a new array.
  • Array patterns also work on strings, Sets, and Map entries.

3.3 Copy, merge and collect with spread and rest

The three dots, ..., have two related jobs. Spread expands existing data into a value or call. Rest gathers values into a variable where a pattern or parameter declares it.

Merge in the order of priority
const defaults = { lang: "en", units: "metric", alerts: true };
const user = { lang: "pt", alerts: false };
const options = { ...defaults, ...user };
// { lang: "pt", units: "metric", alerts: false }

Object spread copies own enumerable properties. When a key appears again, the later value wins. Placing user before defaults would overwrite the user's settings. Spreading null or undefined into an object adds nothing.

Spread copies are shallow. If a nested record changes, copy that level too:

const settings = { theme: "dark", font: { size: 14, family: "serif" } };
const next = {
  ...settings,
  font: { ...settings.font, size: 18 }
};

The outer spread preserves theme. The inner spread preserves family while replacing size. settings.font.size stays 14 because the changed nested record is new.

Replace one record in a list
const todos = [{ id: 1, done: false }, { id: 2, done: false }];
const updated = todos.map(t =>
  t.id === 2 ? { ...t, done: true } : t
);

map makes a new array. The matching item becomes a new object, so updated[1].done is true while todos[1].done stays false. The unchanged first item is reused: updated[0] === todos[0] is true. Independent updates do not require copying unchanged records.

Expand iterable values

In an array literal or function call, spread takes values from an iterable. [...a, ...b] joins their values into a new array. Math.max(...[4, 9, 1]) passes three arguments and returns 9. A plain object cannot be expanded into an array: [...{ a: 1 }] throws a TypeError. Object spread and iterable spread use different rules for what they read.

Gather arguments or remaining properties
function total(...nums) {
  return nums.reduce((sum, n) => sum + n, 0);
}
total(2, 3, 5); // 10
total();        // 0

A rest parameter receives a real array, even when it receives no values. It must be the final parameter, and there can be only one. The older arguments object is not a real array. Arrows have no own arguments, though they can read an enclosing regular function's binding.

Object rest collects own enumerable properties not already selected:

const userRecord = { id: 8, name: "Rae", password: "x9!k" };
const { password, ...publicUser } = userRecord;
// publicUser is { id: 8, name: "Rae" }

This creates a new record and leaves the source unchanged. The rest property must be last. Look at placement to distinguish the jobs: three dots in a literal or call spread outward; three dots in a parameter list or destructuring target gather inward.

Key Takeaways

  • Later properties win when object spreads overlap.
  • Copy each nested level that must change independently.
  • Array and call spread need an iterable.
  • Rest collects remaining arguments, items, or properties and must be last.
  • A map-based update can copy a changed record while reusing unchanged records.
  • Rest parameters are real arrays; the older arguments object is not.
4

Store collections with Map and Set

Some collections need keys that are objects or numbers, and others need to keep each value only once. Map and Set express those needs directly. You will create entries, test membership, follow insertion order, and convert collections to arrays or objects when another operation needs that shape. Equality still matters: two records with matching fields can remain two separate values in either collection.

4.1 Store key-value pairs in a Map

A Map stores associations between keys and values. Unlike a plain object's string keys, a Map key can be a number, an object, or another value without being converted into a string.

Create and maintain entries
const stock = new Map();
stock.set("apples", 12).set("pears", 7);
stock.get("apples"); // 12
stock.has("kiwis");  // false
stock.size;           // 2

set adds an entry or replaces its value and returns the Map for chaining. get returns undefined for a missing key; use has to distinguish that from an entry storing undefined. delete(key) returns whether it removed an entry, and clear() empties the collection.

Bracket notation is a different storage area: stock["plums"] = 5; adds an ordinary property to the Map object. It does not create an entry visible to get, has, or size. Use the Map methods for entries.

Keep key types and identities

m.set(1, "number"); m.set("1", "text"); creates two entries. Plain object keys 1 and "1" would refer to one property. Map keys compare like ===, except NaN matches itself and signed zeros share a key.

const person = { id: 1 };
const notes = new Map([[person, "ready"]]);
notes.get(person);      // "ready"
notes.get({ id: 1 });   // undefined

The second lookup creates a new object. Matching fields do not make it the same key. Keep and reuse the original object reference when using an object as a key.

Iterate and convert pairs
for (const [name, count] of stock) {
  console.log(name, count);
}

A Map yields [key, value] pairs in first-insertion order. Replacing a value does not move its key. keys(), values(), and entries() return iterable views you can loop over or spread into arrays. forEach passes its callback the value first, then the key.

new Map(Object.entries(obj)) turns a record's entries into a Map. Object.fromEntries(map) creates an object; use it when keys are strings, because object keys cannot preserve every Map key type. [...map] creates an array of pairs, useful for saving a Map's entries as JSON.

Count changing keys
const words = "the cat saw the dog and the bird".split(" ");
const counts = new Map();
for (const word of words) {
  counts.set(word, (counts.get(word) ?? 0) + 1);
}

The first occurrence starts at zero, then adds one. Later occurrences read and increase the stored count. counts.get("the") is 3 and counts.size is 6: the size counts distinct words, not all eight occurrences.

Choose a plain object for a record with known fields, especially JSON-friendly data. Choose a Map for changing keys, frequent size checks, or keys with types that must be preserved. Newer Map.groupBy preserves group-key types:

const owner = { name: "Ari" };
const jobs = [{ owner, done: false }, { owner, done: true }];
const byOwner = Map.groupBy(jobs, job => job.owner);
byOwner.get(owner).length; // 2

In older code, loop over jobs. For each owner, use has and set to create a group array once, then get(owner).push(job) to add the record. Keeping owner as an object key distinguishes different owners even when their name fields match. A WeakMap can attach metadata to objects without keeping otherwise unreachable key objects alive. It has no iteration or size. Modern WeakMaps also accept non-registered symbols as keys.

Key Takeaways

  • Map keys preserve their types and compare objects by identity.
  • Use set, get, has, and delete for entries, not bracket notation.
  • size counts entries and iteration follows key insertion order.
  • Maps convert through key-value pairs.
  • A WeakMap can hold object metadata without keeping key objects alive.
  • Map.groupBy preserves group-key types in a newer grouping API.

4.2 Keep unique values in a Set

A Set stores unique values. Its main questions are whether a value is present and how two collections overlap.

Add and test values
const tags = new Set(["js", "css", "js"]);
tags.size;       // 2
tags.add("html");
tags.has("css"); // true

Adding an existing value changes nothing. add returns the Set for chaining. delete(value) returns whether the value was removed, while clear() removes all values. The constructor takes one iterable: use new Set([1, 2, 3]). new Set(1, 2, 3) throws a TypeError because its first input is not iterable. A string is iterable, so new Set("hello") contains "h", "e", "l", and "o".

A Set has no indexed positions. tags[0] is undefined. Loop with for...of, call forEach, or convert to an array for operations such as map and filter. Iteration follows insertion order; removing a value and adding it again places it at the end.

Remove duplicates without sorting
const unique = [...new Set([3, 1, 3, 2, 1])];
// [3, 1, 2]

The Set keeps the first occurrence of each value. Spreading yields those values into an array. Array.from(set) does the same conversion.

Values compare like ===, except NaN matches itself; signed zeros count as one value. Object values compare by identity: new Set([{ id: 1 }, { id: 1 }]).size is 2. To keep one record per ID, build a Map keyed by that ID, then read its values. Later records with the same ID replace earlier records, because set updates an existing entry.

For repeated membership tests in large collections, a Set avoids the one-by-one scan of an array's includes. Use it when presence matters more than position.

Compare two collections

Newer Set methods are available across major browsers from 2024. With a holding 1, 2, 3, 4 and b holding 3, 4, 5:

OperationMeaningResulting values
a.union(b)In either collection1, 2, 3, 4, 5
a.intersection(b)In both3, 4
a.difference(b)In a but not b1, 2
a.symmetricDifference(b)In exactly one1, 2, 5

These return new Sets. isSubsetOf checks that every value is in the other collection. isSupersetOf checks the reverse containment. isDisjointFrom checks that the collections share no values. All three return booleans. For example, new Set([3, 4]).isSubsetOf(a) is true. Difference has a direction: values in b but not a would be only 5.

In an older environment, an intersection can be written as new Set([...a].filter(x => b.has(x))).

Ordinary JSON.stringify(set) produces "{}", losing the stored values. Convert to an array before writing JSON, then use new Set(parsedArray) when reading. A WeakSet can mark objects as processed without keeping otherwise unreachable objects alive. It cannot be iterated. Modern WeakSets also accept non-registered symbols.

Key Takeaways

  • A Set stores each distinct value once and tests presence with has.
  • Convert a Set to an array for indexing or array methods.
  • Object values remain distinct unless their references match.
  • Union, intersection, difference, and symmetric difference answer different membership questions.
  • Difference depends on which Set comes first.
  • Convert Set values to an array before writing JSON.
  • WeakSet can mark objects without keeping them alive.
5

Convert data to and from JSON

JSON carries structured data as text. Converting between that text and live JavaScript values is useful only when you know which information survives the conversion. You will recognize valid JSON, control how values are written, and handle malformed input when reading it. A successful parse also needs a shape check before the program relies on its fields; valid text alone cannot guarantee the expected data.

5.1 Read and write the JSON format

JSON, short for JavaScript Object Notation, is a language-independent text format for structured data. An object is a live JavaScript value; JSON is text that can describe such a value.

Recognize the six value kinds

JSON supports objects, arrays, strings, numbers, booleans, and null. Any of these can be the top-level value. The text 42 is valid JSON even without braces. JSON has no representation for functions, undefined, dates as a distinct type, Map, Set, NaN, or Infinity.

{
  "name": "Amara",
  "age": 34,
  "languages": ["en", "yo"],
  "address": null,
  "member": true
}

The same structural ideas appear in JavaScript literals, but the allowed spelling is stricter. JSON is used in configurations and data exchange. Interchanged JSON uses UTF-8 encoding and the media type application/json; JSON files use the .json extension.

Apply the syntax rules

Every object key must be a string in double quotes. Strings also require double quotes. Commas separate properties or array items, but no comma may follow the last one. Comments are forbidden. Whitespace between values and punctuation is allowed.

TextWhy it fails
{name: "Amara"}The key lacks double quotes.
{'name': 'Amara'}Single quotes are not JSON string delimiters.
{"a": 1,}The final comma has no following property.
{"a": undefined}Undefined is not a JSON value.

Numbers use decimal notation. 1.5e3, -0.25, and 0 are valid. 07, +1, and 0x1F are invalid because JSON forbids leading zeros, a leading plus, and hexadecimal notation.

Escape characters inside strings

Use \" for a double quote, \\ for a backslash, \n for a newline, and \t for a tab inside a JSON string. A raw line break cannot appear inside a JSON string. Let JSON.stringify generate escapes from actual string values so embedded quotes cannot break the output.

A Unicode escape consists of a backslash, the letter u, and four hexadecimal digits for one UTF-16 code unit. For example, \u00e9 represents é. A character beyond that range may need two such escapes; it can also be written directly in the string. The quotes around JSON text in JavaScript source are a separate layer from the quotes inside the JSON itself.

Preserve identifiers and avoid duplicate keys

JavaScript numbers preserve every integer in the safe range from -9007199254740991 through 9007199254740991. Outside that range some integers remain exact and others round. Parsing '{"id":9007199254740993}' gives an id value of 9007199254740992. Store long identifiers as JSON strings when their exact digits matter.

Object keys should be unique within each JSON object. JavaScript's JSON.parse keeps the last occurrence when a key repeats: parsing '{"a":1,"a":2}' produces { a: 2 }. Distinct properties cannot be recovered from that repeated name.

Key Takeaways

  • JSON is text with six value kinds, including primitives at the top level.
  • Keys and strings need double quotes; comments and trailing commas are invalid.
  • JSON numbers exclude hexadecimal notation, leading plus signs, and leading zeros.
  • Escapes represent special characters inside strings.
  • Use strings for long identifiers whose exact digits must survive.
  • Repeated object keys are unsafe; JSON.parse keeps the last value.

5.2 Turn values into JSON text with JSON.stringify

JSON.stringify converts supported JavaScript values into JSON text. Decide what the text must preserve before converting a record with methods or collection values.

Write compact or indented text
const person = { name: "Amara", age: 34, tags: ["admin"] };
const text = JSON.stringify(person);
// '{"name":"Amara","age":34,"tags":["admin"]}'

The result is a string, so typeof text is "string". Own enumerable string-keyed properties are written in Object.keys order. JSON.stringify(person, null, 2) adds line breaks and two spaces of indentation per level. The second argument is null because no replacer is being used. Numeric indentation is capped at 10 spaces; a string used for indentation is limited to its first 10 characters.

Predict dropped and changed values
const data = {
  a: undefined,
  b: () => 1,
  c: NaN,
  d: [undefined, Infinity]
};
JSON.stringify(data); // '{"c":null,"d":[null,null]}'

An object property holding undefined, a function, or a symbol is omitted. In an array, such a value becomes null, preserving that position. NaN and Infinity become null in either place. At the top level, unsupported values such as undefined or a function make stringify return undefined, not JSON text.

A Date writes its ISO UTC string through toJSON. Ordinary Map and Set entries are not written, so either collection becomes {}. Convert Set values to an array, and Map entries to pairs or a string-keyed object, before writing them.

A circular reference and an ordinary BigInt such as 10n cause a TypeError. Shared references that do not form a cycle can be serialized, but JSON will not preserve their shared identity.

Choose data with a replacer
const user = { name: "Rae", email: "rae@example.com", password: "x9!k", age: 31 };
JSON.stringify(user, ["name", "email"]);
// '{"name":"Rae","email":"rae@example.com"}'

A replacer array selects property names. A replacer function transforms values as they are visited, beginning with the whole input under the key "":

JSON.stringify(user, (key, value) =>
  key === "password" ? undefined : value
);

Returning undefined omits an object property, makes an array position null, or causes an overall undefined result when returned for the root. Return the unchanged value for data you want to keep.

An object's toJSON() method can choose its representation. For { celsius: 21, toJSON() { return { c: this.celsius }; } }, stringification writes '{"c":21}'.

Do not build JSON by joining quoted strings yourself: embedded quotes or newlines can break its syntax. Do not stringify already serialized text expecting an object representation either. Stringifying '{"a":1}' encodes a string; parsing that result once returns a string, not an object.

Key Takeaways

  • JSON.stringify usually returns JSON text; top-level unsupported values can return undefined.
  • Unsupported object properties disappear, while unsupported array items become null.
  • Dates become strings; Map and Set need explicit conversion.
  • Circular structures and ordinary BigInts throw TypeError.
  • Replacers and toJSON control the written representation.
  • Stringifying JSON text again encodes a string, not its contained object.

5.3 Turn JSON text into values with JSON.parse

JSON.parse reads JSON text and returns the value it describes. Parsing checks text syntax; checking whether the result fits your program is a separate task.

Read the value, then inspect its fields
const data = JSON.parse('{"city":"Lima","tempC":19.5}');
data.tempC + 1; // 20.5

The outer single quotes delimit a JavaScript string. The double quotes inside it belong to JSON. Parsing creates a plain object with a numeric tempC property. A text describing an array produces an array; primitive JSON texts produce their matching primitive values.

Data that is already an object needs no parse step. JSON.parse({ a: 1 }) first converts that argument to "[object Object]", then throws a SyntaxError. JSON.parse(undefined) also fails because "undefined" is not valid JSON text.

Handle invalid text
function safeParse(text, fallback) {
  try {
    return JSON.parse(text);
  } catch (error) {
    return fallback;
  }
}
safeParse("{ok:true}", null); // null

Malformed input, including an empty string, single-quoted JSON strings, unquoted keys, and trailing commas, throws a SyntaxError. Catching that failure gives the program a deliberate fallback instead of stopping it. A successfully parsed null is also possible, so choose how the caller will distinguish a fallback from valid data when that difference matters.

Restore selected values with a reviver

A date stored as text returns as a string. An array of key-value pairs remains an array until new Map(pairs) turns it into a Map. Functions omitted during writing cannot be recovered from the remaining text.

const text = '{"title":"Dentist","due":"2026-03-01T09:00:00.000Z"}';
const event = JSON.parse(text, (key, value) =>
  key === "due" ? new Date(value) : value
);

The reviver visits inner values before their containers, ending with the whole result under key "". Returning a value replaces the current value. Returning undefined removes that property; returning it for the root makes the entire parse result undefined. Forgetting return value for other keys can therefore erase the data you meant to keep.

Check shape and plan a round trip

Valid JSON can still have the wrong types. Parsing '{"temp":"19"}' leaves a string, so data.temp + 1 would concatenate to "191". Check required fields and types, such as typeof data?.temp === "number", before calculating. Convert with Number only when the intended format permits a numeric string; optional chaining and ?? can handle fields that may be absent.

For const settings = { theme: "dark", volume: 0.8, recent: ["a.txt"] };, stringification produces 48 characters of JSON text. Parsing it restores the same field data, but loaded === settings is false because parsing creates new objects. A Date returns as a string, an undefined-valued object property disappears, and an unconverted Set returns as {}. Convert special values before writing and restore their intended types after reading.

Key Takeaways

  • JSON.parse reads text and throws SyntaxError for malformed JSON.
  • Successful parsing does not check the required fields or their types.
  • A reviver visits children first and must return values you want to keep.
  • JSON round trips create new objects and preserve only representable data.
  • Restore collection and date types explicitly after parsing.

Objects and JSON examples

5 Walkthroughs

1

Follow a shipment record

16 steps
Create, inspect, and copy a nested record while tracking which values are shared.
  1. 1
    IntroductionBuild a record you can inspect

    A shipment combines an ID, a destination, and package records. You will build it, inspect its fields, and trace shared and copied data. Two names can hold one reference to the same record, and a shallow copy still shares nested levels, so an update can reach data you meant to keep apart.

    The shipment examples continue across slides unless a step explicitly creates a separate object.

  2. 2
    ExampleCreate the shipment
    const shipment = {
      id: "s12",
      destination: "Porto",
      delayMin: 0
    };
    

    The literal creates three properties. Delay is measured in minutes, including a valid zero. shipment.destination reads "Porto". Const permits property updates.

  3. 3
    ExampleChoose a field while the program runs
    const field = "destination";
    shipment[field] = "Braga";
    shipment.checked = true;
    

    Bracket access evaluates field and updates the destination property. Assignment also adds checked because it did not exist before. The shipment still has the same identity; neither operation replaces the outer object. Writing shipment.field would instead create a separate property literally named "field".

  4. 4
    ChallengeRead a computed name

    What does shipment[field] read now, and does the shipment own a field property?

    ---reveal---

    It reads "Braga". The variable field contains the key "destination", so both the update and the read reach that property. Object.hasOwn(shipment, "field") is false because no literal field property was created. A variable used to choose a property does not itself become part of the record.

  5. 5
    ExplanationCheck zero without losing it

    shipment.delayMin is zero, so a truthiness condition skips its body. The property still exists.

    Object.hasOwn(shipment, "delayMin") returns true. Use an own-property check for stored fields; the in operator also includes inherited properties.

  6. 6
    ExampleRead the entries
    const pairs = Object.entries(shipment);
    const count = Object.keys(shipment).length;
    

    There are now four own enumerable string-keyed fields, including checked. Entries gives one [key, value] pair for each. You can loop with for (const [key, value] of pairs) to inspect both parts. A direct for...of loop over the plain shipment object would throw because that object is not iterable.

  7. 7
    ChallengeSeparate existence from value

    Suppose shipment.note = undefined; is added. What do shipment.note and Object.hasOwn(shipment, "note") return?

    ---reveal---

    They return undefined and true. Assignment creates the property even though its value is undefined. Deleting the property would change the existence result to false while a later read would still produce undefined. Value alone cannot distinguish these two situations.

  8. 8
    AnalogyTrace two routes to one record

    A reference works like a route to a record: copying the route gives another way to reach the same destination. It does not build a second record.

    const alias = shipment;
    alias.checked = false;
    

    shipment.checked is now false too. Both variable names reach one object, so alias === shipment is true. Matching references explain the shared update; no synchronization step ran between two copies.

  9. 9
    ExampleAdd data that a shallow copy will share
    shipment.packages = [{ massG: 450 }, { massG: 700 }];
    const shallow = { ...shipment };
    shallow.destination = "Faro";
    shallow.packages[0].massG = 500;
    

    The new outer record has its own destination, so shipment.destination remains Braga. Its packages value is the same array reference as before. Following that shared path changes the first package mass in the original shipment to 500 too.

  10. 10
    ExampleMake a supported deep copy
    const deep = structuredClone(shipment);
    deep.packages[0].massG = 600;
    

    The first original package remains at 500 g because the deep copy has its own nested array and package objects. This record contains only supported data. If it contained a function-valued property, structuredClone would throw DataCloneError instead of silently dropping that method.

  11. 11
    ChallengePredict a freeze boundary

    Run these statements. Does the nested assignment throw, and what is the second mass afterward?

    Object.freeze(shipment);
    shipment.packages[1].massG = 750;
    

    ---reveal---

    It succeeds and sets the mass to 750. Freezing shipment protects its own properties but not the referenced array or its records. Reassigning shipment.destination in strict mode would throw TypeError; this nested assignment reaches a different, unfrozen object.

  12. 12
    ExampleCalculate the current total
    const totalG = shipment.packages.reduce(
      (sum, pack) => sum + pack.massG, 0
    );
    

    After the previous updates, the original shipment contains masses 500 and 750 g. Reduce starts at zero, adds 500, then 750, producing 1250. The total is calculated from the current packages. If their masses change later, run the calculation again; this stored number does not recalculate itself.

  13. 13
    TipGuard an optional path
    const contactName = shipment.contact?.name ?? "Unassigned";
    

    There is no contact property in the record, so optional chaining yields undefined and the fallback supplies "Unassigned". This path is allowed to be absent. Keep required fields visible to validation instead of quietly defaulting all errors. The nullish fallback also preserves an intentionally empty name, unlike a logical OR fallback.

  14. 14
    ChallengeKeep a valid zero

    What do shipment.delayMin ?? 10 and shipment.delayMin || 10 return?

    ---reveal---

    The first returns 0; the second returns 10. Nullish coalescing replaces only null or undefined, while logical OR replaces every falsy value. This shipment has a recorded zero-minute delay, so treating it as missing would introduce a delay that the stored data never reported.

  15. 15
    ChallengeCombine a copy with an optional update

    A new record is made with const next = { ...shipment };, then next.packages[0].massG = 520;. What happens to shipment's first mass, and is next the same outer object?

    ---reveal---

    The original mass becomes 520 because the nested array is shared. next === shipment is false because spread created a new outer object. Copying the outer record separates its own assignments but does not make an entire nested path independent.

  16. 16
    RecapChoose the check for the task

    Choose a property by literal or computed key, and distinguish existence from truthiness. Before an update, trace whether its path reaches shared data.

    Copy nested levels that must be independent. Recalculate derived totals after their inputs change.

2

Keep an action connected to its object

12 steps
Trace method calls, captured context, and callbacks with a small counter.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionFollow an action as it moves
  2. 2
    ExampleCreate and call an action
  3. 3
    ChallengeRead the action without running it
  4. 4
    ExampleBorrow the regular function
  5. 5
    ChallengeDetach the borrowed method
  6. 6
    Key InsightPut an arrow inside the method
  7. 7
    ExampleWrap the needed call
  8. 8
    ExampleBind a callback once
  9. 9
    ChallengeChoose immediate or later execution
  10. 10
    TipGive an existing binding priority
  11. 11
    ChallengeFollow a bound method through another object
  12. 12
    RecapRead the calling form
3

Unpack options and build an updated record

14 steps
Read patterns from the outside in, then copy only the levels that must change.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionRead compact syntax in small steps
  2. 2
    ExampleSelect and rename fields
  3. 3
    ExampleEnter a nested pattern
  4. 4
    ChallengeRead a renamed nested field
  5. 5
    ChallengeKeep null distinct from missing
  6. 6
    ExampleUnpack a returned range
  7. 7
    ExampleCollect the tail into its own array
  8. 8
    ChallengeSwap values before using them
  9. 9
    ExampleMerge defaults with overrides
  10. 10
    ExampleCopy the changing nested level
  11. 11
    ConnectionRemove a field from the new record
  12. 12
    ChallengeChoose an independent update
  13. 13
    ChallengeConnect entry patterns with spread
  14. 14
    RecapIdentify the job before tracing values
4

Track counts and unique membership

11 steps
Use Map for changing keyed values and Set for membership, then compare collections.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionChoose what the collection must remember
  2. 2
    ExampleCreate counts with Map methods
  3. 3
    TipKeep entries separate from properties
  4. 4
    ChallengeKeep key types distinct
  5. 5
    ExampleReuse an object key
  6. 6
    ExampleCollect unique labels
  7. 7
    ChallengeDecide whether records are duplicates
  8. 8
    ExampleFind the shared and missing labels
  9. 9
    Key InsightChange the membership question
  10. 10
    ChallengeConnect counts with membership
  11. 11
    RecapMatch the structure to the operation
5

Write, read, and check JSON data

14 steps
Plan a JSON-friendly representation, handle invalid text, and restore intended types.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionPlan both directions of conversion
  2. 2
    ExplanationRead text as text
  3. 3
    ChallengeFix a syntax problem
  4. 4
    ExampleInspect the live settings
  5. 5
    ExampleConvert the collection before writing
  6. 6
    TipLet the serializer escape strings
  7. 7
    ChallengePredict an unsupported array item
  8. 8
    ExampleParse and restore membership behavior
  9. 9
    ExampleHandle malformed text deliberately
  10. 10
    ChallengeCheck a valid but unsuitable value
  11. 11
    ExplanationRestore one field with a reviver
  12. 12
    ChallengePreserve a Map key distinction
  13. 13
    ConnectionKeep cloning and exchange separate
  14. 14
    RecapCheck syntax, shape, and representation

Objects and JSON practice questions

50 Practice Questions in 5 sets

Set 1: Read records and follow values10 Practice Questions

Question 1 of 10

What does sensor[field] return?

const sensor = { tempC: 16, field: "room" };
const field = "tempC";
  1. "room"
  2. 16
  3. "tempC"
  4. undefined
Show answerHide answer
Answer: B
Bracket access uses field's value, "tempC", to select the property holding 16. Dot access in sensor.field would instead read the literal field property and return "room".

Question 2 of 10

What does Object.hasOwn(record, "enabled") return?

const record = { enabled: false };
  1. false
  2. undefined
  3. null
  4. true
Show answerHide answer
Answer: D
The record directly owns enabled, so the check is true. Its stored false value would fail a truthiness test, but that value does not make the property absent.

Question 3 of 10

What are record.region and record.key?

const key = "region";
const record = { [key]: "east" };
record[key] = "west";
  1. "west" and undefined
  2. "east" and "west"
  3. undefined and "west"
  4. "west" and "east"
Show answerHide answer
Answer: A
The computed key creates region and the bracket assignment updates it to "west". No literal key property is created, so reading record.key returns undefined.

Question 4 of 10

What is meter.value after this call?

const meter = {
  value: 7,
  add() { this.value += 3; }
};
meter.add();
  1. 7
  2. 3
  3. 10
  4. undefined
Show answerHide answer
Answer: C
The method call makes meter the receiver and changes value from 7 to 10. Returning undefined from a method without return does not undo its property update.

Question 5 of 10

What does right.read() return?

const left = { name: "Ivo", read() { return this.name; } };
const right = { name: "Nia", read: left.read };
  1. "Ivo"
  2. "Nia"
  3. undefined
  4. A TypeError
Show answerHide answer
Answer: B
The regular function is called through right, so this is right and the returned name is "Nia". The function was first written in left, but that location does not bind its receiver. Returning "Ivo" would confuse the original storage location with the object used in the current call.

Question 6 of 10

What are title and score after destructuring?

const { name: title, score = 8 } = { name: "Reef", score: null };
  1. "Reef" and null
  2. "Reef" and 8
  3. undefined and 8
  4. undefined and null
Show answerHide answer
Answer: A
The pattern reads the property name into the local variable title. Its default for score is used only for undefined, so the explicit null remains null. Replacing null with 8 would apply the broader nullish-default rule to destructuring, which has a narrower undefined-only trigger.

Question 7 of 10

What value does third receive?

const [, , third] = [4, 9, 12, 18];
  1. 4
  2. 9
  3. 12
  4. 18
Show answerHide answer
Answer: C
The two empty positions skip 4 and 9, leaving 12 for third. Selecting 9 would skip only one position; the variable name itself does not select an index.

Question 8 of 10

What are m.size and m.get(2)?

const m = new Map([[2, "number"], ["2", "text"]]);
  1. 1 and "text"
  2. 2 and "text"
  3. 1 and "number"
  4. 2 and "number"
Show answerHide answer
Answer: D
A Map preserves key types, so numeric 2 and string "2" occupy two separate entries. Looking up numeric 2 returns "number". A size of 1 would be the result of treating both keys as the same string, which is how those names behave as ordinary object properties.

Question 9 of 10

A data file must preserve the exact ID digits 9007199254740997 and parse as JSON. What record text meets both needs?
  1. {"id":9007199254740997,"active":true}
  2. {id:"9007199254740997",active:true}
  3. {"id":"9007199254740997","active":true}
  4. {"id":'9007199254740997',"active":true}
Show answerHide answer
Answer: C
Double-quoted keys and lowercase true make the record valid JSON, and a string keeps every ID digit. The unquoted large integer rounds during JavaScript parsing even though its syntax is valid. Unquoted keys or single quotes around the ID fail the JSON syntax check.

Question 10 of 10

What text is returned?

JSON.stringify({
  note: undefined,
  values: [undefined, NaN],
  ok: false
});
  1. '{"values":[null,null],"ok":false}'
  2. '{"note":null,"values":[null,null],"ok":false}'
  3. '{"values":[],"ok":false}'
  4. '{"values":[null,null]}'
Show answerHide answer
Answer: A
The undefined-valued object property is omitted, while undefined in an array becomes null. NaN also becomes null, and the valid boolean false remains. Keeping note as null would incorrectly use the array rule for an object property; deleting array positions would change their structure.
Set 2: Apply patterns and preserve receivers10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

What happens when this text is parsed?

JSON.parse('{"mode":"fast","mode":"slow"}');
  1. It returns { mode: "fast" }.
  2. It returns { mode: "slow" }.
  3. It throws a SyntaxError.
  4. It returns { mode: ["fast", "slow"] }.
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

What does trip.host?.name ?? "Guest" return?

const trip = { host: null };
  1. null
  2. undefined
  3. "Guest"
  4. A TypeError
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

What are the final values of a.level and a === b?

const a = { level: 2 };
const b = a;
b.level = 5;
  1. 5 and true
  2. 2 and false
  3. 5 and false
  4. 2 and true
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

What are source.mode and source.layout.width afterward?

const source = { mode: "day", layout: { width: 40 } };
const copy = { ...source };
copy.mode = "night";
copy.layout.width = 60;
  1. "night" and 60
  2. "day" and 40
  3. "night" and 40
  4. "day" and 60
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

What function can be passed to map so each call updates tally?

const tally = { n: 0, tick() { return ++this.n; } };
const result = ["x", "y"].map(/* callback */);
  1. tally.tick
  2. () => tally.tick()
  3. tally.tick()
  4. () => tally.tick
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

This code runs at module top level. What happens when card.read() is called?

const card = { title: "Basin", read: () => this.title };
  1. It throws a TypeError.
  2. It returns "Basin".
  3. It returns undefined.
  4. It throws a ReferenceError.
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

After next.panel.width = 90, what are next.mode and base.panel.width?

const base = { mode: "wide", panel: { width: 50 } };
const next = { mode: "small", ...base, panel: { ...base.panel } };
  1. "small" and 50
  2. "wide" and 90
  3. "small" and 90
  4. "wide" and 50
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

What value does size receive?

const { size = 6 } = {};
  1. undefined
  2. 6
  3. null
  4. 0
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

What array does this expression produce?

[...new Set(["red", "blue", "red", "green", "blue"])];
  1. ["blue", "green", "red"]
  2. ["red", "green", "blue"]
  3. ["red", "blue", "green"]
  4. ["red", "blue", "red", "green", "blue"]
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

What is the final value of loaded?

const loaded = JSON.parse('{"count":4,"tag":"a"}', (key, value) => {
  if (key === "count") return value * 2;
});
  1. undefined
  2. { count: 8, tag: "a" }
  3. { count: 8 }
  4. {}
Show answerHide answer

Save this lesson to see this answer.

Set 3: Transform collections and recover data10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

What object does the expression create?

Object.fromEntries(
  Object.entries({ north: 3, south: 5 })
    .map(([key, value]) => [key, value + 2])
);
  1. { north: 5, south: 7 }
  2. { north: 3, south: 5 }
  3. { north: 6, south: 10 }
  4. { 5: "north", 7: "south" }
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

What does options.limit ?? 12 return?

const options = { limit: 0 };
  1. 12
  2. undefined
  3. 0
  4. null
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

What must add return for basket.add("pear").add("fig") to use the same basket twice?

const basket = {
  items: [],
  add(item) { this.items.push(item); /* return here */ }
};
  1. this.items
  2. item
  3. this.items.length
  4. this
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

What array is returned in this module?

function scale(n) { return this.factor * n; }
const twice = scale.bind({ factor: 2 });
const obj = { factor: 9, twice };
[obj.twice(3), twice.call({ factor: 5 }, 4)];
  1. [27, 20]
  2. [6, 8]
  3. [6, 20]
  4. [27, 8]
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

What is returned?

function combine(first, ...others) {
  return [first, others.length];
}
combine(...[7, 3, 8, 2]);
  1. [7, 4]
  2. [[7, 3, 8, 2], 0]
  3. [7, 3]
  4. [7, 2]
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

What are items[0].done, next[1] === items[1], and next === items?

const items = [{ id: 4, done: false }, { id: 9, done: false }];
const next = items.map(item =>
  item.id === 4 ? { ...item, done: true } : item
);
  1. true, true, false
  2. false, true, false
  3. false, false, false
  4. false, true, true
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

Why is lookup.get({ code: 8 }) undefined?

const key = { code: 8 };
const lookup = new Map([[key, "ready"]]);
  1. The lookup uses a different object reference.
  2. Map converts both keys into different strings.
  3. Map ignores object keys when entries are created.
  4. The key must first be converted into JSON text.
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

What are the sizes of a.difference(b) and a.symmetricDifference(b)?

const a = new Set([2, 4, 6]);
const b = new Set([4, 8]);
  1. 1 and 1
  2. 3 and 2
  3. 2 and 4
  4. 2 and 3
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

What text does this produce?

JSON.stringify({ label: "Arc", count: 3 }, ["label"]);
  1. '{"label":"Arc","count":3}'
  2. '{"label":"Arc"}'
  3. '["label"]'
  4. '{"label":"Arc","count":null}'
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

This module runs without a reviver. What are typeof loaded.due and loaded === original?

const original = { due: new Date("2026-07-12T00:00:00.000Z") };
const loaded = JSON.parse(JSON.stringify(original));
  1. "object" and false
  2. "string" and true
  3. "object" and true
  4. "string" and false
Show answerHide answer

Save this lesson to see this answer.

Set 4: Diagnose updates and calls10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

What are base.inner.n, clone.inner.n, and copy.inner.n at the end?

const base = { inner: { n: 2 } };
const copy = { ...base };
const clone = structuredClone(base);
Object.freeze(base);
copy.inner.n = 7;
clone.inner.n = 9;
  1. 2, 9, 7
  2. 9, 9, 9
  3. 2, 9, 2
  4. 7, 9, 7
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

What array does Object.keys(data) return?

const data = { z: 1, "10": 2, "02": 3, "2": 4, a: 5 };
  1. ["z", "10", "02", "2", "a"]
  2. ["2", "10", "z", "02", "a"]
  3. ["02", "2", "10", "z", "a"]
  4. ["2", "02", "10", "z", "a"]
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

What is totalG, and does changing heavy.massG also change load.items[1].massG?

const load = { items: [
  { massG: 120, qty: 2 },
  { massG: 300, qty: 1 }
] };
const heavy = load.items.find(x => x.massG > 200);
const totalG = load.items.reduce((s, x) => s + x.massG * x.qty, 0);
heavy.massG = 350;
  1. 540; yes, it becomes 350.
  2. 590; yes, it becomes 350.
  3. 540; no, it stays 300.
  4. 420; yes, it becomes 350.
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

What does b.labels() return in this module?

const a = {
  prefix: "A",
  names: ["oak"],
  labels() { return this.names.map(n => this.prefix + n); }
};
const b = { prefix: "B", names: ["elm"], labels: a.labels };
  1. ["Aoak"]
  2. ["Aelm"]
  3. ["Belm"]
  4. It throws a TypeError.
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

What is typeof box.open?

const box = { open() { return "opened"; } };
  1. "function"
  2. "string"
  3. "object"
  4. "undefined"
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

What happens when read() and read({ prefs: null }) are called separately?

function read({ prefs: { theme = "light" } = {} } = {}) {
  return theme;
}
  1. Both calls return "light".
  2. The first returns "light"; the second throws TypeError.
  3. The first throws TypeError; the second returns null.
  4. The first returns undefined; the second returns null.
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

What is tail?

const [head, ...tail] = [6, 8, 11];
  1. [6, 8, 11]
  2. 8
  3. [11]
  4. [8, 11]
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

What are m.size and [...m.keys()]?

const m = new Map([["p", 1], ["q", 2]]);
m.set("p", 4);
m["r"] = 5;
m.delete("q");
m.set("q", 6);
  1. 3 and ["p", "r", "q"]
  2. 2 and ["q", "p"]
  3. 2 and ["p", "q"]
  4. 3 and ["p", "q", "r"]
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

Why does JSON.parse('{"depthM":+7}') fail?
  1. JSON numbers cannot begin with a plus sign.
  2. JSON numbers cannot be whole numbers.
  3. JSON property names cannot contain capitals.
  4. JSON objects must also contain a string value.
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

What are result.count and result.total?

const result = JSON.parse('{"count":"6","total":12}',
  (key, value) => key === "count" ? Number(value) : value
);
result.total += result.count;
  1. "6" and "126"
  2. 6 and "126"
  3. 6 and 18
  4. undefined and NaN
Show answerHide answer

Save this lesson to see this answer.

Set 5: Combine data choices and conversion rules10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

What are a, b, and rest?

let a = 3, b = 8;
[a, b] = [b, a];
const [, ...rest] = [a, b, 13];
  1. 8, 8, [8, 13]
  2. 8, 3, [8, 3, 13]
  3. 8, 3, [3, 13]
  4. 3, 8, [8, 13]
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

What is Object.hasOwn(record, "old") after these statements?

const record = { old: undefined };
delete record.old;
  1. true
  2. false
  3. undefined
  4. It throws TypeError.
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

What are a === b, a.left === a.right, and b.left === b.right?

const shared = { n: 1 };
const a = { left: shared, right: shared };
const b = JSON.parse(JSON.stringify(a));
  1. false, true, true
  2. true, true, true
  3. false, true, false
  4. false, false, false
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

What does saved() return?

const maker = {
  n: 5,
  make() { return () => this.n; }
};
const other = { n: 8, make: maker.make };
const saved = other.make().bind({ n: 12 });
  1. 5
  2. 12
  3. undefined
  4. 8
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

What does fixed.apply({ offset: 30 }, [4]) return?

function add(a, b) { return this.offset + a + b; }
const fixed = add.bind({ offset: 2 }, 7);
  1. 13
  2. 41
  3. 36
  4. NaN
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

After publicCopy.profile.name = "New", what is source.profile.name, and does source still own secret?

const source = { secret: "s", profile: { name: "Old" } };
const { secret, ...publicCopy } = source;
  1. "Old"; yes.
  2. "New"; yes.
  3. "New"; no.
  4. "Old"; no.
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

What is the final value of counts.get("moss")?

const counts = new Map();
for (const word of ["moss", "fern", "moss", "moss"]) {
  counts.set(word, (counts.get(word) ?? 0) + 1);
}
  1. 1
  2. 2
  3. 4
  4. 3
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

What are records.size and ids.size?

const x = { id: 7 };
const records = new Set([x, x, { id: 7 }]);
const ids = new Set([...records].map(r => r.id));
  1. 2 and 1
  2. 1 and 1
  3. 3 and 1
  4. 2 and 2
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

What is typeof JSON.parse(text)?

const text = JSON.stringify('{"x":3}');
  1. "string"
  2. "object"
  3. "number"
  4. "undefined"
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

A Map must retain separate numeric key 3 and string key "3" with values "n" and "s". What pair of conversions preserves them through JSON?
  1. Write JSON.stringify(Object.fromEntries(m)); read new Map(Object.entries(JSON.parse(text))).
  2. Write JSON.stringify([...m]); read new Map(JSON.parse(text)).
  3. Write JSON.stringify(m); read new Map(Object.entries(JSON.parse(text))).
  4. Write JSON.stringify([...m.keys()]); read new Map(JSON.parse(text)).
Show answerHide answer

Save this lesson to see this answer.

Stuck on your own question? Homework Help

Key Terms

79 Key Terms, each with its definition

A

apply
apply executes a function immediately with an explicit this value and an array containing its ordinary arguments.
format.apply(reading, ["m", 2]) supplies the same receiver and values as format.call(reading, "m", 2).
Array destructuring
Array destructuring reads values from successive iterable positions into a square-bracket pattern.
const [low, high] = bounds; relies on bounds placing its lower value before its higher value.
Array rest element
An array rest element gathers remaining values into a new array and must end the pattern.
const [leader, ...others] = names; separates the first name from an array of the remaining names.
Array-index key
An array-index key is a standard integer string from 0 through 4294967294, ordered numerically before other own string keys.
The key "3" sorts before "12" in Object.keys; "03" stays among ordinary string keys.
Arrow wrapper
An arrow wrapper is an arrow callback whose body performs the intended method call later, keeping that call's receiver explicit.
Passing () => counter.tick() to map makes each callback invoke tick through the counter object.

B

bind
bind returns a new function with a fixed this value and optional leading arguments, leaving execution until the new function is called.
Saving const next = meter.step.bind(meter); creates a callback that keeps the meter receiver.
Borrowed method
A borrowed method is a regular function taken from another object and used through a new receiver without changing the function itself.
Assigning b.read = a.read lets b.read() use b's data when the function reads through this.
Bracket notation
Bracket notation evaluates the expression inside square brackets to select a property key.
With const field = "heightM";, person[field] selects the person's height measurement.

C

call
call executes a function immediately with an explicit this value, followed by ordinary arguments supplied one at a time.
format.call(reading, "m", 2) gives format the reading receiver and two separate arguments.
Circular reference
A circular reference is a path of object references that returns to an object already on that path, which ordinary JSON cannot encode.
Adding record.self = record makes ordinary JSON.stringify throw a TypeError for that record.
Computed property name
A computed property name evaluates a bracketed expression to name a property in a literal.
With const code = "es";, { [code]: "Hola" } creates a property named "es".

D

Deep copy
A deep copy creates independent nested data as well as a new outer value, subject to the types supported by the copying method.
After a supported structuredClone, editing a copied nested field leaves the original field unchanged.
Derived value
A derived value is calculated from other stored data, such as a total from line items, and can become stale if stored separately.
Computing a route's distance from its legs keeps the result consistent after a leg changes.
Destructuring default
A destructuring default replaces a selected undefined value, including a missing field or position.
In const { count = 2 } = { count: null };, the explicit null survives the default.
Detached method
A detached method is a function read from an object and passed or stored without preserving the object in its later calling expression.
const read = sensor.read; keeps the function, but read() does not supply sensor as receiver.
Difference
A difference contains values from a first collection that are absent from a second, so exchanging the collections can change the result.
active.difference(blocked) keeps active values that do not appear in the blocked Set.
Dot notation
Dot notation selects the literal property name after a dot.
profile.name always selects the property named "name", even if a variable called name exists.

E

Entry pair
An entry pair is a two-item sequence whose first value is a property or collection key and whose second value is its associated data.
Object.entries({ lake: 5 }) produces [["lake", 5]], ready for a key-value loop.
Escape sequence
An escape sequence uses a backslash and following characters to represent a special character or UTF-16 code unit inside a JSON string.
Stringifying a message with a newline writes an escape instead of placing a raw line break inside its string.

G

globalThis
globalThis provides a standard way to refer to the global object, which non-strict regular functions use for a plain call.
A browser's global object is window, but a strict plain function call still gets undefined as this.

I

Immutable update
An immutable update builds new containers for the data that changes while preserving the original containers and any unchanged shared values.
A map callback can return a spread copy for the changed record and reuse every unaffected record.
Indentation argument
The indentation argument is stringify's third argument, controlling spaces or a short string used to format nested JSON text.
Passing null as the replacer and 2 as the third argument makes each nested level use two spaces.
Insertion order
Insertion order is the sequence in which distinct keys or values first enter a collection, retained by Map and Set iteration.
Updating a Map entry keeps its place; deleting it and inserting the key again puts it last.
Intersection
An intersection contains only the values present in both collections, leaving out values that occur in just one of them.
Intersecting active IDs with approved IDs finds the IDs that meet both membership conditions.
Iterable
An iterable supplies a sequence of values to operations such as for...of or array spread.
Arrays, strings, Maps, and Sets can be unpacked this way; a plain record cannot.
Iterable spread
Iterable spread expands an iterable into separate array elements or call arguments, reading its values in sequence.
Math.max(...measurements) supplies each array item as a separate argument to the function.

J

JSON
JSON is a language-independent text format that represents objects, arrays, strings, numbers, booleans, and null using strict syntax.
The text {"ready":true} describes a record, while true alone is also a valid whole JSON text.
JSON string
A JSON string is text data delimited by double quotes, with escapes for characters such as quotation marks and line breaks.
A text value containing a quotation mark needs an escape inside its surrounding JSON double quotes.
JSON.parse
JSON.parse reads JSON text and returns its represented JavaScript value, throwing a SyntaxError when the text is malformed.
Parsing a record with numeric count 4 allows arithmetic, while a quoted count remains a string.
JSON.stringify
JSON.stringify serializes supported JavaScript values into JSON text, optionally selecting data and adding readable indentation.
JSON.stringify(settings, null, 2) writes a more readable form without changing the represented settings.

L

Lexical this
Lexical this is the surrounding this value retained by an arrow where it is created, unaffected by a later receiver or binding.
An arrow created inside a running method can keep that method's object during an array callback.

M

Map
A Map is a collection of key-value entries with keys of any type, preserving the order in which its keys were first inserted.
Numeric key 5 and string key "5" remain distinct entries when stored in the same Map.
Map entry
A Map entry associates one key with one value and is maintained with Map methods, independently of ordinary object properties.
stock.set("tea", 4) changes an entry; stock["tea"] = 4 changes an ordinary property.
Map.groupBy
Map.groupBy is a newer method that groups iterable items into arrays inside a Map, preserving the type and identity of group keys.
Grouping records by a shared owner object lets that same owner reference retrieve its group.
Membership test
A membership test asks whether a key or value is present in a collection, independently of the data stored under that key.
map.has(key) can find an existing entry even when map.get(key) returns undefined.
Method
A method is a function-valued property called through an object.
Calling tracker.advance() runs the tracker's action, while reading tracker.advance returns a function.
Method chaining
Method chaining calls a method on the value returned by the preceding call.
list.add("a").add("b") works when the first call returns an object with the next add method.
Method shorthand
Method shorthand defines a regular method directly by its name, parameters, and body in a literal.
The member reset() { this.n = 0; } defines an action without a separate colon and function keyword.

N

Nested data
Nested data places objects or arrays inside other objects or arrays, so a path must follow several steps to reach a value.
report.readings[0].tempC reads a list, its first record, and then a temperature field.
Nested pattern
A nested pattern selects values from an inner structure while unpacking its outer structure.
const { owner: { name } } = item; needs an owner object and declares only name.
Nullish coalescing
Nullish coalescing supplies a fallback only when its left operand is null or undefined, preserving other falsy values.
settings.volume ?? 1 keeps an explicit volume of zero, while settings.volume || 1 replaces it.

O

Object destructuring
Object destructuring reads selected properties into variables by matching their names.
const { city, tempC } = reading; creates two local variables from a weather record.
Object identity
Object identity is the distinction between one particular object and every other object, regardless of matching field values.
Two separately created records with id: 9 compare unequal with === despite matching data.
Object literal
An object literal creates an object directly with curly braces and key-value properties.
const reading = { tempC: 22 }; creates a record containing one temperature measurement.
Object rest
Object rest collects own enumerable properties not already named in a destructuring pattern into a new shallow object.
const { password, ...publicRecord } = user; leaves user intact while excluding password from the copy.
Object spread
Object spread copies own enumerable properties into a new literal, with later occurrences overwriting earlier values of the same key.
{ ...base, active: true } copies base and then supplies the final value for active.
Object.freeze
Object.freeze prevents additions, deletions, and changes to an object's own properties but does not freeze nested objects.
In strict code, assigning a frozen record's top-level status throws a TypeError.
Object.fromEntries
Object.fromEntries creates an object from key-value pairs, making it useful after a transformation of entries or a compatible Map.
Transforming each pair's value and rebuilding the object can convert a whole record of measurements.
Object.groupBy
Object.groupBy is a newer method that groups iterable items into arrays under callback-selected keys in an object with no prototype.
Grouping tasks by status gives one array for completed tasks and another for pending tasks.
Optional chaining
Optional chaining stops a property access, indexed access, or call when the checked value is null or undefined, yielding undefined.
record.owner?.name allows a missing owner without trying to read a name from undefined.
Options object
An options object passes named settings to a function instead of relying on argument positions.
A function receiving { color, size } lets callers state what each setting means without positional ordering.
Own property
An own property belongs directly to an object, so an own-property check does not include properties reached through inheritance.
Object.hasOwn({}, "toString") is false even though "toString" in {} is true.

P

Partial application
Partial application fixes some arguments now to make a function that takes the remaining arguments when it is called later.
multiply.bind(null, 4) makes a function that multiplies its next argument by four.
Property
A property pairs an object key with a value, including data or a function.
The active property in { active: false } exists even though its value is a false boolean.
Property existence
Property existence means that a key is present, independently of whether its stored value is truthy, falsy, or undefined.
A record with count: 0 still has a count property; testing that value as a condition misses it.

R

Receiver
A receiver is the object supplied as this for a regular method call.
In account.describe(), account is the receiver that the method uses to read its fields.
Reference
A reference connects a variable or property to an object, allowing several names to reach and change the same underlying value.
Assigning const backup = record; gives another way to reach record without copying its fields.
Renaming pattern
A renaming pattern reads a property into a different local name using a colon.
const { title: label } = page; creates label without declaring a variable called title.
Replacer
A replacer is the second stringify argument: an array of property names to include or a function that transforms visited values.
Returning undefined for a private object property omits it, while returning the original value keeps other data.
Rest parameter
A rest parameter gathers remaining function arguments into a real array and must be the final parameter in the declaration.
function total(...values) can use reduce on values even when the call supplies no arguments.
Reviver
A reviver is a JSON.parse callback that transforms values after their children, using its return values to replace or remove them.
A reviver can restore a known date field and return all unrelated values unchanged.
Round trip
A round trip writes data into a representation and reads it back, preserving only the information that representation can express.
A JSON round trip creates new objects and returns a Date as text unless a conversion restores its type.

S

Safe integer
A safe integer is an integer from -9007199254740991 through 9007199254740991, where JavaScript Number keeps every integer distinct and exact.
An identifier longer than the safe range can be sent as a JSON string to preserve every digit.
Serialization
Serialization converts live values into a representation that can be stored or exchanged, such as JSON text produced by stringify.
A settings object must be serialized before a text-only destination can receive its structure.
Set
A Set is a collection of unique values of any type that preserves insertion order for iteration and has no indexed positions.
Adding "ready" twice keeps one value, while adding two separate matching records keeps both.
Shallow copy
A shallow copy creates a new outer object or array while retaining references to any nested objects stored within it.
Updating a copied record's nested address can also update the original record's shared address.
Shape check
A shape check verifies that a parsed value has the fields, container kinds, and value types the program needs before using it.
Valid JSON containing a string count still needs checking before a program treats that field as a number.
Shorthand property
A shorthand property takes its name and value from a same-named variable.
With a variable called city, the literal { city } creates the same field as { city: city }.
Skipped position
A skipped position consumes a value without naming it in an array destructuring pattern.
const [, second] = colors; skips the first color and names only the next yielded value.
Strict plain call
A strict plain call invokes a strict regular function without supplying a receiver, leaving its this value undefined.
Detaching a method into f and calling f() in a module can fail when it tries to read this.name.
structuredClone
structuredClone copies supported nested JavaScript values, including circular structures, but rejects function values.
A record containing plain arrays can be cloned; including a method causes a DataCloneError.
Subset
A subset is a collection whose every value also belongs to another collection, even when the two collections are equal.
A required-permissions Set is a subset of granted permissions only when every required value is granted.
Symmetric difference
A symmetric difference contains values present in exactly one of two collections, excluding every value they share.
Comparing two tag Sets this way finds tags that belong to one list but not to both.
SyntaxError
A SyntaxError signals invalid syntax, such as malformed text supplied to JSON.parse.
A trailing comma in external JSON can be caught with try...catch so the program can handle the failure.

T

this
The this keyword names a function's current context; regular functions usually get it from the call and arrows keep an outer context.
The same unbound regular read method can read different objects when called through different receivers.
toJSON
A toJSON method returns an object's chosen JSON-friendly representation, which stringify writes in place of the original object.
A measurement object can return a smaller plain record containing the fields meant for exchange.

U

Union
A union contains every value present in either of two collections, with shared values included only once in the resulting Set.
If one group has Ana and Bo and another has Bo and Cai, their union has all three people.

W

WeakMap
A WeakMap associates values with object or non-registered symbol keys without keeping otherwise unreachable keys alive.
A WeakMap can attach metadata to an object without adding fields to that object or exposing an entry list.
WeakSet
A WeakSet records object or non-registered symbol membership without keeping otherwise unreachable members alive and cannot be iterated.
A WeakSet can mark processed objects without retaining them solely because they were once processed.

About this lesson

By StudeliReport a mistake

Make your own Study Materials

Turn your notes, slides and documents into Study Guides, Practice Questions, Flashcards and 6 more tools.

Get Started