js-dojo

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

Source

'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');

Solution