Warm-up · Activity 1 of 7
Warm-up from lesson I2.1. What does this print?
const proto = {hello() { return `hi ${this.name}`; }};
const a = Object.create(proto);
a.name = "Ada";
console.log(a.hello(), Object.hasOwn(a, "hello"));// I2.2 · ~34 min · Intermediate
After this lesson you write a class with a constructor and methods, create objects with new, and know where its methods live.
You will be able to
Warm-up · Activity 1 of 7
const proto = {hello() { return `hi ${this.name}`; }};
const a = Object.create(proto);
a.name = "Ada";
console.log(a.hello(), Object.hasOwn(a, "hello"));Predict · Activity 2 of 7
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
const p = new Point(1, 2);
console.log(typeof Point, Object.keys(p).join(","));Practice · Activity 3 of 7
class Dog {
constructor(name) {
this.name = name;
}
}
const rex = ____ Dog("Rex");Practice · Activity 4 of 7
Practice · Activity 5 of 7
class Dog {
constructor(name) {
this.name = name;
}
bark() {
return `${this.name}: woof`;
}
}
const a = new Dog("Rex");
const b = new Dog("Bello");
console.log(a.bark === b.bark, b.bark());Brain teaser · Activity 6 of 7
class Strange {
constructor() {
this.kind = "instance";
return {kind: "plain object"};
}
}
const s = new Strange();
console.log(s.kind, s instanceof Strange);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
The class Book stores a title and a page count in its constructor and has two methods. Two books are made with new. The program prints them, then checks what the class really is: a function, whose prototype holds describe and isLong for every book. At the end it calls Book without new.
main.js
class Book {
constructor(title, pages) {
this.title = title;
this.pages = pages;
}
describe() {
return `${this.title} (${this.pages} pages)`;
}
isLong() {
return this.pages > 300;
}
}
const novel = new Book("Momo", 304);
const guide = new Book("JS Pocket", 120);
console.log(novel.describe(), novel.isLong());
console.log(guide.describe(), guide.isLong());
console.log(novel);
// Under the hood: a function, and methods on its prototype.
console.log(typeof Book);
console.log(Object.getPrototypeOf(novel) === Book.prototype);
console.log(novel.describe === guide.describe, Object.hasOwn(novel, "describe"));
console.log(novel instanceof Book, novel instanceof Object);
try {
Book("Oops", 1); // forgot new
} catch (error) {
console.log(`${error.name}: ${error.message}`);
}
Run it with
node main.jsOutput
Momo (304 pages) true
JS Pocket (120 pages) false
Book { title: 'Momo', pages: 304 }
function
true
true false
true true
TypeError: Class constructor Book cannot be invoked without 'new'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
Temperature should store its celsius value so that toFahrenheit() can use it: new Temperature(100).toFahrenheit() is 212. The starter’s constructor only puts the value into a local variable, so the method reads this.celsius as undefined and returns NaN. Fix the constructor.
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: toFahrenheit gives NaN. What is this.celsius inside it?
A const inside the constructor disappears when the constructor ends. Data an object keeps goes on this.
this.celsius = celsius;
One way to solve it. Yours can look different and still pass the checks.
export class Temperature {
constructor(celsius) {
this.celsius = celsius;
}
toFahrenheit() {
return (this.celsius * 9) / 5 + 32;
}
}
Install ECMAScript 2026 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.js
export class Temperature {
constructor(celsius) {
const value = celsius;
}
toFahrenheit() {
return (this.celsius * 9) / 5 + 32;
}
}
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {Temperature} from './main.js';
test('100 degrees Celsius are 212 Fahrenheit', () => {
const got = new Temperature(100).toFahrenheit();
assert.equal(got, 212, `new Temperature(100).toFahrenheit() gave ${got}`);
});
test('The celsius value is an own property', () => {
const t = new Temperature(-40);
assert.equal(t.celsius, -40, `t.celsius is ${t.celsius}`);
assert.equal(Object.hasOwn(t, 'celsius'), true, 'celsius should be set on this in the constructor');
});
test('toFahrenheit stays on the prototype', () => {
const t = new Temperature(0);
assert.equal(Object.hasOwn(t, 'toFahrenheit'), false, 'toFahrenheit should be a method of the class, not an own property');
});
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
Basket has add(name, price), which returns the basket so calls can be chained, and total(). The starter keeps every item in one array ITEMS outside the class, so all baskets share it. Give each basket its own array: create this.items in a constructor and use it in both methods. Two baskets must not see each other’s items.
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: a new basket already has items. Where are they stored?
Data that belongs to one object goes on this, set in the constructor.
constructor() { this.items = []; } and then this.items.push(…) and this.items.reduce(…).
One way to solve it. Yours can look different and still pass the checks.
export class Basket {
constructor() {
this.items = [];
}
add(name, price) {
this.items.push({ name, price });
return this;
}
total() {
return this.items.reduce((sum, item) => sum + item.price, 0);
}
}
Install ECMAScript 2026 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.js
const ITEMS = [];
export class Basket {
add(name, price) {
ITEMS.push({ name, price });
return this;
}
total() {
return ITEMS.reduce((sum, item) => sum + item.price, 0);
}
}
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {Basket} from './main.js';
test('add can be chained and total adds the prices', () => {
const basket = new Basket().add('tea', 3).add('cake', 4);
assert.equal(basket.total(), 7, `the total is ${basket.total()}`);
});
test('A new basket is empty', () => {
new Basket().add('tea', 3);
const fresh = new Basket();
assert.equal(fresh.total(), 0, `a new basket has the total ${fresh.total()}`);
});
test('Two baskets keep their own items', () => {
const a = new Basket();
const b = new Basket();
a.add('bread', 2);
b.add('milk', 1);
assert.deepEqual([a.total(), b.total()], [2, 1], `the totals are ${a.total()} and ${b.total()}`);
});
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.jsclass Book {
constructor(title) {
this.title = title;
}
}
const book = Book("Momo");
console.log(book.title);
What Node.js prints
TypeError: Class constructor Book cannot be invoked without 'new'Why, and the fix
A class can only be used with new, which makes the object that this points to. Called like a plain function, it throws at once. Write const book = new Book("Momo");.
const p = new Point(1, 2);
console.log(p.x);
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
What Node.js prints
ReferenceError: Cannot access 'Point' before initializationWhy, and the fix
A function declaration can be called above the line that declares it; a class cannot. Until its declaration has run, the class name is in the temporal dead zone, like a let or const. Move the class above the code that uses it, or into a module you import.
class Dog {
constructor(name) {
this.name = name;
}
bark() {
return `${this.name}: woof`;
}
}
const rex = new Dog("Rex");
const bark = rex.bark;
console.log(bark());
What Node.js prints
TypeError: Cannot read properties of undefined (reading 'name')Why, and the fix
this is set by the call, not by where the method came from. bark() has no object before the dot, and class code is strict, so this is undefined. Call it as rex.bark(), or, when you must pass it on, as a callback, wrap it: () => rex.bark().
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.