Chapter 4 · Real programs
Prototypes: objects that share
Step 1 of 22 · Concept
Objects that share
By the end of this lesson you can:
- give an object methods that use
this, - link one object to another, so it can use the other's properties,
- see where
pushandmapreally live.
Lesson 15 gave each account its own functions. Here many objects share one.
Step 2 of 22 · Concept
A method uses this
const rex = {
name: "Rex",
speak() {
return `${this.name} says woof`;
},
};
console.log(rex.speak());
speak is a method. Inside it, this is the object before the dot of the call: rex.speak() runs it with
this set to rex. With no object before the dot, this is undefined.
Step 3 of 22 · Quiz
Whose name?
tom gets the same function as rex. Type the missing line.
const rex = {
name: "Rex",
speak() {
return `${this.name} says woof`;
},
};
const tom = {
name: "Tom",
speak: rex.speak,
};
console.log(rex.speak());
console.log(tom.speak());
Step 4 of 22 · Demo
See it run
Run it and compare with your answer. Then write speak in the long form, speak: function () { ... }: the same
output. Last, write it as an arrow function, speak: () => `${this.name} says woof`,, and run it: no this.
Step 5 of 22 · Exercise
Your turn: a book that describes itself
Give each book a method describe() that returns its title and pages. The program must print:
Dune, 412 pages
Maps, 96 pages
Inside the method, title alone is a variable, and there is none: ReferenceError: title is not defined. The book's
properties are read through this: this.title.
Write describe() { ... } in each object, after pages, with a return and a template literal. Not an arrow
function: it has no this.
return `${this.title}, ${this.pages} pages`;
Step 6 of 22 · Concept
Every object has a hidden link
const animal = {
legs: 4,
speak() {
return `${this.name} makes a sound`;
},
};
const rex = Object.create(animal);
rex.name = "Rex";
Every object has a hidden link to its prototype: another object, or null at the end of the chain.
Object.create(animal) makes an empty object linked to animal. Object.getPrototypeOf(x) follows the link:
Object.getPrototypeOf(rex) === animal is true.
Step 7 of 22 · Concept
Reading looks along the chain
rex has only name. To read rex.legs, Node:
- looks on
rexitself; - when it is not there, follows the link and looks on the prototype, and stops when it finds the key;
- goes on, link by link, up to
Object.prototype, whose link isnull. If no object has the key, the answer isundefined.
So rex.legs is 4, and rex.colour is undefined. A method found on the chain still runs with this set to
rex: rex.speak() gives Rex makes a sound.
Step 8 of 22 · Trace
Watch a lookup climb
Each frame is one step of a read. The objects the read has looked at are marked not here; where it finds the key is marked in colour. Press Next.
const animal = {
legs: 4,
speak() {
return `${this.name} makes a sound`;
},
};
const rex = Object.create(animal);
rex.name = "Rex";
console.log(rex.legs);
console.log(rex.colour);
console.log(rex.speak());- Line 9 reads
rex.legs. The read looks onrexitself first: it has onlyname. - Not there, so it follows the link:
animalhaslegs. Line 9 printed4. - Line 10 reads
rex.colour: not onrex. One link up, not onanimaleither. - One more link: not on
Object.prototype. Its link isnull, so the read givesundefined, and line 10 printed it. - Line 11 reads
rex.speak: not onrex. One link up, it is found onanimal. - The call gets a frame. Its
thispoints atrex, the object before the dot, not atanimal. this.nameis found onrexitself.speakreturns"Rex makes a sound".- Line 11 printed what
speakreturned. The program is finished.
Step 9 of 22 · Quiz
Found, or not?
pup is linked to rex, which is linked to animal. What does this program print?
const animal = { legs: 4 };
const rex = Object.create(animal);
rex.name = "Rex";
const pup = Object.create(rex);
console.log(pup.legs, pup.name);
Step 10 of 22 · Demo
See it run
Run it and compare with your answer. Then log Object.getPrototypeOf(pup) === rex too.
Step 11 of 22 · Concept
Own properties
A property on the object itself is its own property. Object.hasOwn(obj, key) asks about the object alone, not
its chain:
| you write | gives |
|---|---|
Object.hasOwn(rex, "name") | true |
Object.hasOwn(rex, "legs") | false: legs is on animal |
Object.hasOwn(animal, "legs") | true |
console.log(rex) | { name: 'Rex' }: own properties only |
Object.keys(rex) | [ 'name' ] (lesson 11) |
So console.log never shows what an object finds on its chain.
Step 12 of 22 · Demo
Ask, then call what is not there
Run it. The last line calls rex.fly(), which no object on the chain has. The read gives undefined, and calling
it stops the program.
Step 13 of 22 · Concept
Writing stays on the object
const bird = Object.create(animal);
bird.legs = 2;
Writing a property does not change the prototype: it makes or changes the object's own property. bird.legs is now
2, and animal.legs stays 4.
bird's own legs hides the prototype's: shadowing. delete bird.legs uncovers it.
Step 14 of 22 · Quiz
Which legs changed?
Type the missing line.
const animal = { legs: 4 };
const bird = Object.create(animal);
bird.legs = 2;
console.log(bird.legs, animal.legs);
console.log(Object.hasOwn(bird, "legs"));
Step 15 of 22 · Demo
See it run
Run it and compare with your answer. Then add these lines at the end and run again: b sees what a pushed.
const team = { tags: [] };
const a = Object.create(team);
const b = Object.create(team);
a.tags.push("fast");
console.log(b.tags, Object.hasOwn(a, "tags"));
Step 16 of 22 · Exercise
Your turn: pets that share a voice
Two pets share animal's speak(). The cat has a sound of its own.
- Give
animala methodspeak()that returns`${this.name} says ${this.sound}`. - Give the cat its own
sound,"meow".
The program must print:
Tom says meow
Nemo says ...
Change animal.sound and every pet without a sound of its own reads the new one: Nemo says meow too. Write the
sound to the cat itself.
speak() { ... } goes inside animal, after sound. The cat's sound is a property of cat, set after
cat.name, as name is.
cat.sound = "meow";
Step 17 of 22 · Concept
Arrays and text use prototypes too
const xs = [3, 1, 2];
const name = "Ada";
Every array is linked to Array.prototype, where push, map and filter live. Object.getPrototypeOf(xs) ===
Array.prototype is true; Object.hasOwn(xs, "push") is false.
Text sits in its box, with no link (lesson 2). A method called on text is looked up on String.prototype.
At the end of each chain, Object.prototype gives every object toString. So String({}) and a template literal
of an object, `${{}}`, give [object Object].
Step 20 of 22 · Check yourself
Check yourself: shapes that describe themselves
No starter code this time.
- Make a
shapewith a methoddescribe()that returns`${this.name}: ${this.w * this.h}`. - Make
square(3 by 3) andrect(2 by 4) withObject.create(shape), each with its ownname,wandh. - Log both descriptions, then
Object.hasOwn(square, "describe").
The output must be:
square: 9
rect: 8
false
One object holds the method; the two shapes hold only their data. Each shape is made empty by Object.create, then
gets its three properties, one line each.
Step 21 of 22 · Challenge · optional
Challenge: a trick for every pet
Optional.
The pets already exist. After them, give animal a method trick() that returns `${this.name} rolls over`:
every pet can do it at once.
The output must be:
Tom rolls over
Nemo rolls over
false
A method is a property whose value is a function. Write it to animal as you write any property: animal.trick = ....
The pets read it through their link.
The value must be a function with its own this, so not an arrow function. Use the long form from the step A method uses
this: function () { ... }.
animal.trick = function () {
Step 22 of 22 · Recap
Recap
- In a method,
thisis the object before the dot of the call. - Every object has a hidden link to its prototype;
Object.create(proto)links a new one toproto. - Reading climbs the chain to
null, then givesundefined; writing stays on the object and shadows. push,mapandfilterlive onArray.prototype, shared by every array.- One method on a prototype serves many objects; closures make new functions for each.
Next: lesson 17 writes the same links with class.
Review
3 questions from this lesson, in a new order each time. Answer from memory: 2 of 3 right on the first try passes. On a day after you finish the lesson, a pass earns 15 XP.
The review needs JavaScript.
const rex = {
name: "Rex",
speak() {
return `${this.name} says woof`;
},
};
const tom = {
name: "Tom",
speak: rex.speak,
};
console.log(rex.speak());
console.log(tom.speak());
const book = {
title: "Dune",
pages: 412,
// describe() returns "Dune, 412 pages"
};
const guide = {
title: "Maps",
pages: 96,
// the same describe() here
};
console.log(book.describe());
console.log(guide.describe());
const animal = { legs: 4 };
const rex = Object.create(animal);
rex.name = "Rex";
const pup = Object.create(rex);
console.log(pup.legs, pup.name);
const animal = {
legs: 4,
speak() {
return `${this.name} makes a sound`;
},
};
const rex = Object.create(animal);
rex.name = "Rex";
console.log(rex);
console.log(Object.hasOwn(rex, "legs"));
console.log(Object.hasOwn(animal, "legs"));
console.log(Object.getPrototypeOf(rex) === animal);
rex.fly();
const animal = { legs: 4 };
const bird = Object.create(animal);
bird.legs = 2;
console.log(bird.legs, animal.legs);
console.log(Object.hasOwn(bird, "legs"));
const animal = {
sound: "...",
// 1. speak() returns "<name> says <sound>"
};
const cat = Object.create(animal);
cat.name = "Tom";
// 2. the cat's own sound is "meow"
const fish = Object.create(animal);
fish.name = "Nemo";
console.log(cat.speak());
console.log(fish.speak());
const proto = {
deposit(amount) {
this.balance += amount;
},
};
const a = Object.create(proto);
a.balance = 0;
const b = Object.create(proto);
b.balance = 10;
a.deposit(5);
b.deposit(5);
console.log(a.balance, b.balance, a.deposit === b.deposit);
function makeAccount() {
let balance = 0;
const deposit = (amount) => {
balance += amount;
};
const getBalance = () => balance;
return { deposit, getBalance };
}
const c = makeAccount();
const d = makeAccount();
c.deposit(5);
d.deposit(1);
const same = c.deposit === d.deposit;
console.log(c.getBalance(), d.getBalance(), same);
console.log([1].push === [2].push, String({}));
// 1. shape with describe()
// 2. square and rect, linked to shape
// 3. log both descriptions and the hasOwn answer
const animal = {
sound: "...",
speak() {
return `${this.name} says ${this.sound}`;
},
};
const cat = Object.create(animal);
cat.name = "Tom";
const fish = Object.create(animal);
fish.name = "Nemo";
// after the pets: give animal a method trick()
console.log(cat.trick());
console.log(fish.trick());
console.log(Object.hasOwn(cat, "trick"));