Warm-up · Activity 1 of 7
Warm-up from lesson B5.3: what does this print?
const item = { name: "tea", price: 3 };
const copy = { ...item };
copy.price = 4;
console.log(item.price, copy.price);// B5.4 · ~30 min · Beginner
After this lesson you turn data into JSON text and back, write JSON that parses, and check what you loaded.
You will be able to
Warm-up · Activity 1 of 7
const item = { name: "tea", price: 3 };
const copy = { ...item };
copy.price = 4;
console.log(item.price, copy.price);Predict · Activity 2 of 7
const text = '{"name": "Ada", "age": 36}';
console.log(text.name, JSON.parse(text).name);Practice · Activity 3 of 7
const text = '{"count": 2}';
const data = JSON.____(text);
console.log(data.count + 1);Practice · Activity 4 of 7
Practice · Activity 5 of 7
console.log(JSON.stringify({ a: 1, b: undefined, c: [1, undefined] }));Brain teaser · Activity 6 of 7
const order = { total: NaN, when: new Date(0) };
const copy = JSON.parse(JSON.stringify(order));
console.log(copy.total, typeof copy.when);Apply · Activity 7 of 7
Check your work against this list
Read the worked example, then write the exercises. Your code runs in your browser or on your computer and is never uploaded.
Worked example
A reading list is turned into JSON text and back. The text is one string with no spaces; with an indent it spreads over lines. Parsing gives real objects again, a round trip gives an independent copy, and an object with NaN, undefined, a Date and a method shows what JSON cannot hold. The last part, a preview of module I4, catches the SyntaxError from JSON with single quotes.
main.js
// A reading list saved as JSON text and loaded back.
const list = [
{ title: "Dune", pages: 412, read: true },
{ title: "Emma", pages: 474, read: false }
];
const text = JSON.stringify(list);
console.log(text);
console.log(typeof text, Array.isArray(list));
console.log(JSON.stringify(list[0], null, 2));
const loaded = JSON.parse(text);
console.log(loaded[1].title, Array.isArray(loaded), typeof loaded[0]);
const copy = JSON.parse(JSON.stringify(list));
copy[0].read = false;
console.log(list[0].read, copy[0].read);
const odd = { score: NaN, note: undefined, when: new Date(0), show() {} };
console.log(JSON.stringify(odd));
try {
JSON.parse("{'title': 'Dune'}");
} catch (error) {
console.log(error.name + ": " + error.message);
}
Run it with
node main.jsOutput
[{"title":"Dune","pages":412,"read":true},{"title":"Emma","pages":474,"read":false}]
string true
{
"title": "Dune",
"pages": 412,
"read": true
}
Emma true object
true false
{"score":null,"when":"1970-01-01T00:00:00.000Z"}
SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2)Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.
Exercise 1 of 2
parseScores(text) should parse JSON text and return the array it holds. If the text holds anything else, such as an object or a number, it should return an empty array, []. isRecord(value) should return true only for a plain object: not for null, not for an array, not for a number. The starter trusts the text, and its isRecord says yes to null and arrays. Fix both with Array.isArray, typeof and a check for null.
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.
Run the tests: parseScores gives back the object it parsed, and isRecord(null) is true, because typeof null is "object".
Keep the parsed value in a constant, then return Array.isArray(data) ? data : [].
For isRecord, add two checks with &&: value !== null and !Array.isArray(value).
One way to solve it. Yours can look different and still pass the checks.
// The array in the JSON text, or [] when the text holds something else.
export function parseScores(text) {
const data = JSON.parse(text);
return Array.isArray(data) ? data : [];
}
// true only for a plain object: not null, not an array.
export function isRecord(value) {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
Install ECMAScript 2026 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.js
// The array in the JSON text, or [] when the text holds something else.
export function parseScores(text) {
return JSON.parse(text);
}
// true only for a plain object: not null, not an array.
export function isRecord(value) {
return typeof value === "object";
}
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {parseScores, isRecord} from './main.js';
test('parseScores returns the array in the text', () => {
const got = parseScores('[3, 5, 8]');
assert.deepEqual(got, [3, 5, 8], `parseScores('[3, 5, 8]') returned ${JSON.stringify(got)}, expected [3,5,8]`);
});
test('parseScores returns [] for an object', () => {
const got = parseScores('{"scores": [3, 5]}');
assert.deepEqual(got, [], `parseScores of an object returned ${JSON.stringify(got)}, expected []`);
});
test('parseScores returns [] for a number', () => {
const got = parseScores('7');
assert.deepEqual(got, [], `parseScores('7') returned ${JSON.stringify(got)}, expected []`);
});
test('isRecord is true for an object', () => {
assert.equal(isRecord(JSON.parse('{"a": 1}')), true, 'isRecord of an object should be true');
});
test('isRecord is false for null and for an array', () => {
assert.equal(isRecord(JSON.parse('null')), false, 'isRecord(null) should be false');
assert.equal(isRecord(JSON.parse('[1]')), false, 'isRecord of an array should be false');
});
package.json
{
"type": "module"
}
package.json tells Node.js that the .js files are modules; keep it in the folder.
Run the program:
node main.jsRun the checks (needs learnrun.js in the same folder):
node --testDownload learnrun.jsExercise 2 of 2
toJson(data) should return the JSON text of data, indented by two spaces, ready to save in a file. copyData(data) should return a copy that can change, nested arrays included, without changing data. The data is plain: objects, arrays, strings, numbers and booleans. The starter writes everything on one line, and its spread copy shares the nested arrays. Fix both with JSON.stringify and JSON.parse.
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads the JavaScript runner (up to 0.1 MB) and keeps it cached. Your code runs in your browser’s own engine and stays on your device.
Run the tests: toJson gives one long line, and pushing to the copy's items changes the original too.
JSON.stringify takes the indent as its third argument; the second one is null.
Turn data into text and parse it back: JSON.parse(JSON.stringify(data)) builds every nested array anew.
One way to solve it. Yours can look different and still pass the checks.
// The JSON text of data, indented by two spaces.
export function toJson(data) {
return JSON.stringify(data, null, 2);
}
// A copy whose nested arrays and objects are its own.
export function copyData(data) {
return JSON.parse(JSON.stringify(data));
}
Install ECMAScript 2026 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.js
// The JSON text of data, indented by two spaces.
export function toJson(data) {
return JSON.stringify(data);
}
// A copy whose nested arrays and objects are its own.
export function copyData(data) {
return { ...data };
}
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {toJson, copyData} from './main.js';
test('toJson indents by two spaces', () => {
const got = toJson({ name: 'Ada', tags: ['x'] });
const expected = '{\n "name": "Ada",\n "tags": [\n "x"\n ]\n}';
assert.equal(got, expected, `toJson returned ${JSON.stringify(got)}, expected ${JSON.stringify(expected)}`);
});
test('copyData copies every key', () => {
const got = copyData({ owner: 'Ada', items: ['tea'] });
assert.deepEqual(got, { owner: 'Ada', items: ['tea'] }, `copyData returned ${JSON.stringify(got)}`);
});
test('Changing the nested array of the copy leaves the original alone', () => {
const data = { owner: 'Ada', items: ['tea'] };
const copy = copyData(data);
copy.items.push('milk');
assert.deepEqual(data.items, ['tea'], `the original's items became ${JSON.stringify(data.items)}, expected ["tea"]`);
});
package.json
{
"type": "module"
}
package.json tells Node.js that the .js files are modules; keep it in the folder.
Run the program:
node main.jsRun the checks (needs learnrun.js in the same folder):
node --testDownload learnrun.jsconst text = '{"name": "Ada", "age": 36,}';
const user = JSON.parse(text);
console.log(user.name);
What Node.js prints
SyntaxError: Expected double-quoted property name in JSON at position 26 (line 1 column 27)Why, and the fix
JavaScript allows a comma after the last item, JSON does not. After the comma, JSON.parse expects another key in double quotes and finds }, at position 26 (counting from 0). Remove the comma: '{"name": "Ada", "age": 36}'. JSON written by JSON.stringify never has this problem; it comes from JSON typed by hand.
const text = "{'name': 'Ada'}";
const user = JSON.parse(text);
console.log(user.name);
What Node.js prints
SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2)Why, and the fix
JSON keys and strings take double quotes only, so the single quote at position 1 is not a property name. Write the JSON with double quotes, and wrap the JavaScript string in single quotes or backticks: '{"name": "Ada"}'. Firefox and Safari word this message differently; the position is the clue.
const user = { name: "Ada" };
const data = JSON.parse(user);
console.log(data.name);
What Node.js prints
SyntaxError: "[object Object]" is not valid JSONWhy, and the fix
JSON.parse takes text. Given an object, it first turns it into a string, "[object Object]", which is not JSON. user is already an object: use it directly, user.name. Parse only strings that hold JSON, such as text read from a file or a server, or made by JSON.stringify.
JavaScript in the browser: your browser’s own engine, in a sandboxed worker. Syntax errors are located with acorn 8.18.0, MIT. Licence and source
5 questions, no hints. Score 80% or more to complete the lesson.
Finish every activity above to unlock the exit ticket.