1 [[Prototype]] vs Fn.prototype
GOAL
Separate the two very different things that get called "prototype": (a) the hidden [[Prototype]] link that EVERY object carries — the link property lookup really walks, and (b) the `.prototype` PROPERTY that only functions own, which just declares "anything built by `new me` gets THIS as its hidden link".
CONCEPT
`new Player()` mints a fresh object whose hidden [[Prototype]] is pointed at `Player.prototype`. The instance itself gets NO useful `.prototype` — assigning one only creates a dead own property.
HINT
Object.getPrototypeOf(obj) exposes the hidden link. The two ideas meet in exactly one place: Object.getPrototypeOf(new Fn()) === Fn.prototype.
MIRRORS
Game engines and UI toolkits hand every entity its shared methods through `Entity.prototype.helper = ...`. A mod that writes to `someEntity.prototype` instead is a silent no-op nobody notices until the method is missing on every other entity.
Run
node 01-proto-vs-prototype.cjs — fix the TODOs until it printsSource
'use strict';
// ---------------------------------------------------------------------
// PROTO-DOJO · Module 1 / Exercise 1 — `[[Prototype]]` vs
// `Fn.prototype`
//
// GOAL: Separate the two very different things that get called
// "prototype": (a) the hidden [[Prototype]] link that EVERY
// object carries — the link property lookup really walks, and
// (b) the `.prototype` PROPERTY that only functions own, which
// just declares "anything built by `new me` gets THIS as its
// hidden link".
// CONCEPT: `new Player()` mints a fresh object whose hidden
// [[Prototype]] is pointed at `Player.prototype`. The instance
// itself gets NO useful `.prototype` — assigning one only
// creates a dead own property.
// HINT: Object.getPrototypeOf(obj) exposes the hidden link. The two
// ideas meet in exactly one place: Object.getPrototypeOf(new
// Fn()) === Fn.prototype.
// MIRRORS: Game engines and UI toolkits hand every entity its shared
// methods through `Entity.prototype.helper = ...`. A mod that
// writes to `someEntity.prototype` instead is a silent no-op
// nobody notices until the method is missing on every other
// entity.
//
// Run: node 01-proto-vs-prototype.cjs — fix the TODOs until it prints
// PASS.
// ---------------------------------------------------------------------
const assert = require('node:assert');
function Player(id, name) {
this.id = id;
this.name = name;
this.alive = true;
}
Player.prototype.describe = function () {
return this.id + ': ' + this.name;
};
const knight = new Player('knight', 'Sir Knight');
const rogue = new Player('rogue', 'Shadow Rogue');
// -- TODO 1 -----------------------------------------------------------
// A teammate shipped this to give every Player a knockOut() method. It parses,
// throws nothing… and changes nothing: property lookup on rogue never visits
// an own property called "prototype" that happens to sit on knight. Attach
// knockOut() to the ONE object every Player instance reaches via its hidden link.
knight.prototype = {
knockOut: function () {
this.alive = false;
return this;
},
};
// -- TODO 2 -----------------------------------------------------------
// Replace null with an expression that yields the object rogue's hidden
// [[Prototype]] link points at. Read it OFF THE INSTANCE — don't simply
// re-type `Player.prototype`.
const hiddenLink = null;
// -- TODO 3 -----------------------------------------------------------
// `.prototype` is a property that only means something on constructor functions.
// What is `rogue.prototype`? Replace the string with the actual value.
const instanceDotPrototype = 'replace me';
// -- TODO 4 -----------------------------------------------------------
// True or false: the Player FUNCTION's own hidden link is Player.prototype.
// (Careful — Player is itself an object, and nobody built it with `new
// Player`.)
const fnHiddenLinkIsItsPrototypeProp = true;
// -- checks -----------------------------------------------------------
assert.strictEqual(knight.describe(), 'knight: Sir Knight', 'setup sanity — should never fail');
assert.strictEqual(
typeof rogue.knockOut,
'function',
'TODO 1: knockOut() is parked on an inert own property `knight.prototype` — no lookup ever walks ' +
"through there. Which object DO all Player instances' hidden links point to?"
);
rogue.knockOut();
assert.strictEqual(rogue.alive, false, 'knockOut() should flip alive on the instance it is called on');
assert.strictEqual(
hiddenLink,
Player.prototype,
'TODO 2: the hidden link of an instance is read with Object.getPrototypeOf(...)'
);
assert.strictEqual(
instanceDotPrototype,
rogue.prototype,
'TODO 3: instances have NO meaningful .prototype — that property belongs to functions. ' +
'What does reading a property that is not there give you?'
);
assert.strictEqual(
fnHiddenLinkIsItsPrototypeProp,
Object.getPrototypeOf(Player) === Player.prototype,
'TODO 4: Player.prototype is where INSTANCES point; Player the function-object points at Function.prototype'
);
console.log('PASS — 01-proto-vs-prototype: hidden [[Prototype]] link vs .prototype property untangled');