Skip to content
Theory · Map, Set and JSON · lesson 5 of 9

JSON

Imagine you are moving and want to send a friend a wardrobe by mail. A whole wardrobe will not fit into an envelope. But you can write down on a sheet of paper which boards it is made of, and your friend will build the same wardrobe from that note.

An object in a program is in the same position. An object exists only inside a running program. To save it to a file or send it to a server, it is turned into a string. The String function does not fit this job:

JavaScript
const user = { name: "Ann", age: 30, admin: false };

console.log(String(user)); // [object Object]

const text = JSON.stringify(user);
console.log(text); // {"name":"Ann","age":30,"admin":false}
console.log(typeof text); // string

const copy = JSON.parse(text);
console.log(copy.name, copy.age); // Ann 30

Let's go step by step:

  1. String(user) gives a useless [object Object]: the properties are gone.
  2. JSON.stringify(user) writes all the properties into a string.
  3. typeof text confirms that it is an ordinary string.
  4. JSON.parse(text) reads the string and builds a new object copy from it.

The JSON format

JSON (JavaScript Object Notation) is a text data format. It looks like an object written in JavaScript, but it is stricter: keys and strings go only in double quotes. Values can be objects, arrays, strings, numbers, true, false and null.

Almost every programming language understands this format, so programs exchange data through it. Two functions work with it in opposite directions:

  • JSON.stringify turns a value into a JSON string;
  • JSON.parse does the reverse: it reads a string and builds a new value from it, most often an object.

Reading a JSON string is called parsing. In the example copy is a different object, and copy === user gives false. Like your friend's wardrobe: the same, but not the very one.

What gets lost

This is where the wardrobe analogy stops working. You would notice that some part could not be described on the sheet. JSON.stringify drops such parts silently.

JSON has no undefined, functions, NaN or Infinity, and JSON.stringify drops or replaces them without a warning:

  • a property whose value is undefined or a function disappears from the string entirely;
  • NaN and Infinity are written as null;
  • the Map and Set collections turn into the empty object {}.
JavaScript
const item = { id: 7, note: undefined, price: NaN, show() {} };
console.log(JSON.stringify(item)); // {"id":7,"price":null}

const tags = new Set(["js", "css"]);
console.log(JSON.stringify(tags)); // {}

There is no error, and you notice the loss only when you read the data back. To save a Map, it is first turned into an array of pairs: JSON.stringify([...map]).

A copy through JSON

A JSON string has no link to the original object, and JSON.parse builds every nested object anew. That is why JSON.parse(JSON.stringify(x)) gives a deep copy: a copy in which the nested objects are new too. But it holds only what JSON can store.

The structuredClone function keeps undefined, NaN, Map and Set, but it cannot copy an object with a method:

JavaScript
const order = { id: 1, items: { pen: 2 }, note: undefined };
const viaJson = JSON.parse(JSON.stringify(order));
const clone = structuredClone(order);

order.items.pen = 5;
console.log(viaJson.items.pen, clone.items.pen); // 2 2
console.log("note" in viaJson, "note" in clone); // false true

Let's see what happens here:

  1. viaJson and clone are two deep copies of order.
  2. order.items.pen = 5 changes a nested object of the original.
  3. Both copies print 2: their nested objects are their own, not shared with order.
  4. viaJson has no note property, while clone kept it.

Interviewers often ask how to make a deep copy of an object. A good answer names both ways and what each of them loses.

Common mistake

The string for JSON.parse was written by the rules of JavaScript, not of JSON:

JavaScript
console.log("start");
const settings = JSON.parse("{ size: 'M' }"); // ✗
console.log(settings.size);
TEXT
SyntaxError: Expected property name or '}' in JSON at position 2 (line 1 column 3)

Invalid text does not turn into undefined: the program stops with a SyntaxError, and the last console.log never runs. This is the error text in Node and Chrome; other browsers word it differently.

This is a runtime error despite its name SyntaxError. The program manages to print start: JSON.parse checks the text only when called, and the lines above really have run.

A key without quotes and single quotes are valid in JavaScript, but not in JSON: the correct string is '{"size":"M"}'. Because of such strict rules, JSON strings are usually not written by hand but taken from JSON.stringify.

Key points

  • JSON.stringify turns a value into a JSON string, and JSON.parse builds a new value from a string.
  • undefined, functions, NaN, Infinity, Map and Set are lost or replaced without an error.
  • JSON.parse(JSON.stringify(x)) gives a deep copy of only what JSON can store; structuredClone keeps more.
  • An invalid string in JSON.parse throws a SyntaxError at run time.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way