3 call / apply / bind
GOAL
Put all three explicit-this tools through their paces on one shared name-tag formatter: .call (args listed one by one), .apply (args packed into an array), .bind (build a NEW function with this — and optionally leading args — pre-filled), plus the rule that a bound this is PERMANENT.
CONCEPT
fn.call(thisArg, a, b) — run now, args listed fn.apply(thisArg, [a, b]) — run now, args as one array fn.bind(thisArg, a) — run LATER; hands back a new function with this welded and `a` pre-filled (partial application). Re-aiming a bound function with .call is silently ignored.
HINT
TODO 3 should hand you a name-tag formatter the hud can call later, not a finished name tag — bind manufactures, it never invokes.
MIRRORS
Any engine or UI library you don't own runs YOUR hud/label callback with .call(context) so `this` is the thing being drawn; your own wrapper code pre-binds those callbacks to a specific game before dropping them into a settings object.
Run
node 03-call-apply-bind.cjsSource
'use strict';
// ---------------------------------------------------------------------
// PROTO-DOJO · Module 2 / Exercise 3 — call / apply / bind
//
// GOAL: Put all three explicit-this tools through their paces on one
// shared name-tag formatter: .call (args listed one by one),
// .apply (args packed into an array), .bind (build a NEW
// function with this — and optionally leading args —
// pre-filled), plus the rule that a bound this is PERMANENT.
// CONCEPT: fn.call(thisArg, a, b) — run now, args listed
// fn.apply(thisArg, [a, b]) — run now, args as one array
// fn.bind(thisArg, a) — run LATER; hands back a new
// function with this welded and `a` pre-filled (partial
// application). Re-aiming a bound function with .call is
// silently ignored.
// HINT: TODO 3 should hand you a name-tag formatter the hud can call
// later, not a finished name tag — bind manufactures, it never
// invokes.
// MIRRORS: Any engine or UI library you don't own runs YOUR hud/label
// callback with .call(context) so `this` is the thing being
// drawn; your own wrapper code pre-binds those callbacks to a
// specific game before dropping them into a settings object.
//
// Run: node 03-call-apply-bind.cjs
// ---------------------------------------------------------------------
const assert = require('node:assert');
// One name-tag formatter shared by every player on the roster — `this`
// decides
// which player it speaks for at each call.
function formatNameTag(decimals, suffix) {
return this.name + ' ' + this.score.toFixed(decimals) + (suffix || '');
}
const knight = { name: 'knight', score: 1284.375 };
const rogue = { name: 'rogue', score: 97.5 };
// -- TODO 1 -- invoke formatNameTag ON knight with decimals=1 (no
// suffix), via .call
const viaCall = '???';
// -- TODO 2 -- invoke it ON rogue via .apply — the args arrive
// pre-packed as an array (say, out of a hud settings object):
// decimals=2, suffix=' pts'.
const viaApply = '???';
// -- TODO 3 -- manufacture a reusable hud name-tag formatter:
// this=knight AND decimals=3 pre-filled (partial application). Only
// `suffix` stays open.
const knightNameTag = '???';
// -- checks -----------------------------------------------------------
assert.strictEqual(viaCall, 'knight 1284.4',
'TODO 1: fn.call(thisArg, arg1, arg2, ...) — this first, then args spread out');
assert.strictEqual(viaApply, 'rogue 97.50 pts',
'TODO 2: fn.apply(thisArg, [args]) — the array IS the argument list');
assert.strictEqual(typeof knightNameTag, 'function',
'TODO 3: bind does not call — it manufactures a new function for later');
assert.strictEqual(knightNameTag(' (hero)'), 'knight 1284.375 (hero)',
'TODO 3: decimals pre-filled at bind time, suffix supplied at call time');
assert.strictEqual(knightNameTag(''), 'knight 1284.375',
'TODO 3: reusable — call it as often as you like');
// -- TODO 4 -- a bound this is PERMANENT. Predict the exact name tag
// the hud shows:
const rebindAttempt = '???';
assert.strictEqual(
rebindAttempt,
knightNameTag.call(rogue, '!'),
'TODO 4: .call on a BOUND function cannot re-aim this — the bound target wins and rogue is ignored; ' +
"'!' still lands in the open suffix slot"
);
console.log('PASS — 03-call-apply-bind: explicit this, partial application, and the permanence of bind');