跳转至

08 原型链、this 与 class:活的继承,和它的黑暗面

这一章讲什么

上一章结尾留下一个问题:drawer.toString() 这个方法,drawer 身上根本没有, 凭什么能调用?这一章回答它,并且把答案展开成 JavaScript 对象体系的三大件:

  1. 原型链:属性查找的完整路线——读沿链向上找,写只落在自己身上;
  2. 原型污染:这条链作为攻击面的经典漏洞,以及两种防御;
  3. this:方法怎么知道自己在操作哪个对象;以及 class 语法—— 它「只是糖」,但「又不只是糖」。

这一章是全书对象部分的力学核心:07 章的档案(描述符)管「找到之后」, 本章的链管「怎么找到」,10 章的隐藏类管「怎么找得快」。

顶层全景:一次属性查找的全程

  你写:drawer.label
     │
     ▼
  ① drawer 自己身上有 'label' 吗? ──有→ 按描述符办(数据属性返回值 / 访问器跑 getter)
     │ 没有
     ▼
  ② 沿 [[Prototype]] 指针到原型对象,重复 ①
     │ 还没有
     ▼
  ③ 再沿原型的 [[Prototype]] 往上……直到 null(Object.prototype 的再上一层)
     │
     ▼
  全程没找到 → 返回 undefined

  写(drawer.label = 'x')则不同:数据属性→直接在自己身上建/改;
  链上只有访问器→沿链找到 setter 并执行。读上链,写(多半)不上链。

核心原理

3.1 原型:不是复制,是活的连接

继承的核心机制一句话:每个对象可以有一个原型,原型就是另一个普通对象; 你读一个属性,自己身上没有,就去原型上找;原型上没有,就去原型的原型上找, 直到链的尽头1。书里贯穿的例子是一对共享同一个原型的抽屉:

const drawerPrototype = { open() { console.log('Opening drawer'); } };
const drawer1 = Object.create(drawerPrototype);   // 创建时就指定原型
const drawer2 = Object.create(drawerPrototype);
drawer1.open();   // 'Opening drawer' —— 方法不在自己身上,在原型上
drawer2.open();   // 同一个方法

关键在于这是继承,但不是类继承,也不是复制:原型是个活对象, 连接在运行时一直维护着。Java 和 C++ 的继承关系在编译那一刻就焊死; JavaScript 的原型系统是动态的,动态在两条2:

动态一:改原型,立即生效。 给 drawerPrototype 加一个 close 方法, drawer1 和 drawer2 立刻都会了——方法从来就没被复制到抽屉上, 它们只是指向同一个原型对象3。

动态二:换原型,也行。 Object.setPrototypeOf(drawer, secureDrawer) 可以把一个已存在的对象的原型整个换掉,对象的行为随之改变4。

灵活性是真的,风险也是真的:改动一个共享原型,影响所有继承它的对象—— 包括你以为毫无关系的代码。这个风险有个名字,马上讲5。

3.2 查找算法走查:遮蔽与读写不对称

把查找过程落到具体状态。主线例子:读一次、写一次,看两边各发生什么6。

const drawerPrototype = { label: 'default' };
const drawer = Object.create(drawerPrototype);
console.log(drawer.label);    // ① 读:'default' —— 自己身上没有,沿链在原型上找到
drawer.label = 'tax documents';
console.log(drawer.label);    // ② 再读:'tax documents' —— 自己身上有了,原型的被遮住
console.log(drawerPrototype.label);  // ③ 原型那边:'default',纹丝未动!

三个状态并排看,不对称就清楚了:读沿链向上找到原型的 label; 写却在 drawer 自己身上新建了一个 label,原型的原值原封不动。 从这一刻起,drawer 的 label 遮蔽(shadow)了原型上的同名属性—— 只要新属性不删,原型那个就永久被无视7。

这个「我以为改了继承来的值、其实改的是自己的副本」的不对称, 是开发者(尤其面对不是自己写的代码时)的持续痛点8。

访问器改变写的路线。 如果原型上的 label 是访问器(带 setter), drawer.label = 'x' 就不会在 drawer 上建新属性——引擎沿链找到 setter, 执行它。但注意值落在哪:例子里 setter 是 set label(v) { this._label = ... }, this 是 drawer,所以 _label 被建在 drawer 身上而不是原型身上9。 读上链、写(多半)不上链——这个不对称是恒定的;调试时先分清你面对哪种10。

规范层的算法,书里给了接近引擎视角的伪代码,值得对着看一遍11:

function 原型链查找(object, name) {
  let current = object;
  while (current !== null) {
    const 档案 = Object.getOwnPropertyDescriptor(current, name);  // 只看自己,不递归
    if (档案 != null) {
      if (typeof 档案.get === 'function') return 档案.get.call(object);  // 访问器:调 getter
      return 档案.value;                                               // 数据属性:返回值
    }
    current = Object.getPrototypeOf(current);   // 沿 [[Prototype]] 上一步
  }
  return undefined;                            // 走到链尾(null)也没找到
}

三个细节:每站只看「自己的属性」(所以 hasOwn 的含义内嵌其中); 遇到访问器要以发起查找的对象为 this 调 getter;链尾是 null。 而绝大多数对象的链终点是 Object.prototype(提供 toString、valueOf 等全员共享的方法),它的再上一层就是 null12。

3.3 工具箱:四种「查属性」的方式,别混

写法 查什么 例子(drawer 继承自 drawerPrototype)
Object.getOwnPropertyDescriptor(obj, 'p') 只看自己 原型上的属性查不到
'p' in obj 整条链 'open' in drawer → true;'label' in drawer → false
Object.hasOwn(obj, 'p') 只看自己 Object.hasOwn(drawer, 'open') → false
if (obj.p) 既不查存在、也不查真值,查的是「取出来的值 truthy 吗」 陷阱见下

13

最后一个是最常见的误用:用 if (obj.prop) 判断「属性存不存在」是靠不住的—— prop 存在但值为 0/''/null 时,条件照样不成立。要存在性用 in 或 hasOwn, 要真值性才用真值判断,两个意图别串14。

3.4 原型污染:往根上投毒

现在把第 3.1 节埋的风险引爆。原型污染(prototype pollution)是 JavaScript 的经典漏洞:攻击者往共享原型(最狠的是 Object.prototype)里 注入属性,所有对象沿链查找时都会撞见它15。书里的攻击演示,状态一步步来16:

function openDrawer(drawer) {
  if (drawer.isAuthorized) {              // 安全检查:读 isAuthorized
    console.log(`Opening ${drawer.label}...`);
    return true;
  }
  console.log('Access denied');
  return false;
}
const secureDrawer = { label: 'Confidential Documents' };
openDrawer(secureDrawer);        // ① 'Access denied' —— 没有 isAuthorized,链上也没有
Object.prototype.isAuthorized = true;   // ② 攻击者投毒:往万物之根注属性
openDrawer(secureDrawer);        // ③ 'Opening Confidential Documents...' —— 检查形同虚设!

第一步,secureDrawer 自己没有 isAuthorized,沿链到 Object.prototype 也没有——拒绝。第二步,攻击者把 isAuthorized = true 注进了 Object.prototype。第三步,同一个查找,在链顶找到了 true,放行。 全应用里每一个依赖「这属性不存在」的检查,集体失守17。

第二个变体更贴近日常:往 API 传 options 对象。攻击者只要让 Object.prototype.accessLevel = 'admin',而你的代码写 accessLevel: options.accessLevel || 'normal'—— 调用方没传这个字段,查找沿链捡回 'admin',权限判断被绕过18。

防御有两条,书里建议两头都布19:

  1. 调用侧:构造 options 时显式 __proto__: null,造一个无原型的对象, 链根本不存在,污染无从落地;
  2. API 侧:用 Object.hasOwn(options, 'accessLevel') 逐一确认字段 是调用方自己的,而不是链上捡来的。防御做在 API 内部,不依赖调用方自觉20。

顺带一提:Object.create(null) 造出的对象连 toString 都没有, 所以 Map 的某些场景和「纯数据字典」都爱用它。

3.5 this:调用那一刻才决定

属性找到之后,执行时的 this 是谁?这是 JavaScript 最著名的坑。 this 是运行时才定的:不由函数写在哪里决定,而由怎么调用决定21。 同一个方法,换个调用方式,this 就换人——书里总结为: 对象和方法松散耦合,只在调用那一刻通过 this 连上22。

this 只可能是三种归宿:某个具体对象;全局对象(globalThis); 或 undefined。后两种取决于严格模式(strict mode,一套更严格的校验规则): 非严格模式下,this 没有具体对象时兜底成全局对象(1995 年以来的原始行为); 严格模式下是 undefined。现代代码基本自动处于严格模式(ESM 和 class 体内默认严格)23。

作者的比方值得转述:家人喊一声「他们来了!」,你先得弄清「他们」指谁, 再决定是喜是愁——this 就是那个「他们」,不知道指代就危险24。

还有个技术编辑挑出来的细节:foo.call(1) 这种写法,引擎确实会接受, 但不会真把数字 1 当 this——它会先包一层对象(装箱),this === 1 是 false,typeof this 是 'object'25。语言铁了心维持「this 只能是对象或 undefined」。

八卦一句:this 的灵感来自 Smalltalk 的 self,但实现得如此独特, 连 JavaScript 之父 Brendan Eich 自己都承认「是个错误」—— 这语言当年可是 10 天设计出来的26。

3.6 this 与原型链的合流:共享方法为什么能工作

3.2 节的伪代码里埋了一个承诺:访问器要以「发起查找的对象」为 this 调 getter。 方法同理,而且这正是原型继承能成立的关键27:

const drawerPrototype = {
  open() {
    console.log(`Opening the ${this.label} drawer`);   // this 是谁?
    this.isOpen = true;
  }
};
const drawer1 = Object.create(drawerPrototype);
drawer1.label = 'tax documents';
const drawer2 = Object.create(drawerPrototype);
drawer2.label = 'receipts';
drawer1.open();   // 'Opening the tax documents drawer'
drawer2.open();   // 'Opening the receipts drawer'

open 方法在原型上只有一份,但两次调用 this.label 分别读出 'tax documents' 和 'receipts'——this 指向发起查找的那个对象, 不是找到方法的那个原型28。少了这条规则,共享方法就无法碰到 各自实例的数据,原型继承当场报废29。

3.7 class:是糖,又不只是糖

ES6 引入的 class 语法至今仍有争议——连一部分语言委员会成员都认为 当初不该加30。争议的焦点不在功能,在口味;功能上它的定位, 书里给得非常精准:一种更省字的对象创建与继承写法(术语叫语法糖), 底下转译成的仍是原型机制31。

糖的部分: 下面两种写法完全等价——

class Drawer {                       // class 版
  constructor(label) { this.label = label; }
  open() { console.log(`Opening ${this.label}`); }
}
// ↓ 等价于 ↓
function Drawer(label) { this.label = label; }          // 构造函数版
Drawer.prototype.open = function() { console.log(`Opening ${this.label}`); };

new Drawer('Tax documents') 实际做的事,两版一模一样:

const drawer = Object.create(Drawer.prototype);   // ① 建一个以 Drawer.prototype 为原型的对象
Drawer.call(drawer, 'Tax documents');             // ② 以 drawer 为 this 跑构造函数

32 构造函数本身也是个函数(「函数只是对象」这句话到第 6 章才彻底展开, MEAP 里那一章还不存在);extends 配 super() 只是把原型链的搭建写得更顺眼。 instanceof 检查的本质也是沿原型链找33。

不只是糖的部分: 早期只能拿 _privateData 这种命名约定当「私有」, 约定只是给人看的,挡不住代码;class 引入了 # 前缀的真正私有字段—— #pinCode 在类外彻底不可见:不出现在枚举(逐个列出成员)里,不能点出来,连 getOwnPropertyDescriptor 都查不到它;而且不继承34。 第二个小能力:static(静态)成员——术语是从其他语言借来的, 在 JavaScript 里它的全部含义就是「挂在构造函数这个对象自己身上的属性, 而不是挂在实例或原型上」(SecureDrawer.label 有值、new SecureDrawer().label 是 undefined)35。

作者的判断与证据

有证据的判断:

  • 查找算法、遮蔽、访问器改写的走向,都有带具体状态的代码示例支撑36;
  • 「class 转译成原型机制」有逐行等价的代码对照37。

作者的主张:

  • 「原型污染是经典漏洞」的定性,书中给的是教学化演示,没有引用 CVE 级真实案例; 现实中它确实常年出现在真实漏洞库里,但那是书外证据。
  • 「class 是错误」的争议双方,书中如实并陈,作者自己未站队—— 只陈述它解决了组织问题(初始化与方法收拢在一处)38。

判断(我们的,不是书里的): 「读走链、写留身」的不对称是 JS 对象系统里最 值得记住的一条——「我以为改了配置,其实新建了同名属性」这一类 bug, 根源全在这一句;它比原型污染这类大词更高频。 如果错,会错在: 在只用 class、不写散对象的现代代码风格里,遮蔽的机会大幅减少, 这条不对称的实际杀伤力在下降——但它仍在(任何合并 options 对象的场景),不会归零。

边界与局限

  • this 的「非典型绑定」(call/apply/bind、箭头函数的继承绑定) 全书明确推迟到「函数一章」——MEAP 里那一章不存在,这是本书最大的未完待续。
  • 原型污染的真实攻防(哪类 JSON 解析器、合并函数易中招)书中没展开, 只给了原理和两个防御动作。
  • class 的字段初始化顺序、私有方法的 this 校验等边角,原书未涉及。
  • Object.setPrototypeOf 的性能代价(强制去优化)没有提—— 它和 10 章的内联缓存直接相关,读完 10 章要记得回来自行补上这条。

可带走的

  1. 原型 = 另一个对象;读沿 [[Prototype]] 链上找,找到 null 为止;链顶通常是 Object.prototype。
  2. 写只在自 己身上发生(数据属性),会遮蔽原型;原型是访问器时写沿链找 setter。
  3. 四种查属性方式(getOwnPropertyDescriptor / in / hasOwn / if(obj.p))各查各的,别拿真值性冒充存在性。
  4. 原型污染 = 往 Object.prototype 注属性;防御:Object.hasOwn + null 原型对象,两头都布。
  5. this 由调用方式决定,不由函数定义位置决定;严格模式下无人认领时是 undefined。
  6. 方法经原型链找到后,this 指向发起查找的对象——共享方法的生命线。
  7. class 是原型机制的糖:new 做的事就是 Object.create + call;但 #私有字段、static 是真新增。
  8. 记住 Brendan Eich 的「10 天」:这套对象体系是急就章,它的怪都有历史原因。

原文地图

主题 原书章 原文位置
原型链核心机制 5.3 Prototypical inheritance: the filing cabinet family tree text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:13(搜「prototype, which is simply another object」)
活连接 vs 类继承 同上 text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:44(搜「not through classes or copying」)
改原型立即生效 同上 text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:50(搜「immediately see the change」)
换原型 同上 text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:84(搜「Object.setPrototypeOf」) · :116(搜「bugs or security vulnerabilities」)
遮蔽与读写不对称 同上 text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:141(搜「label: 'default'」) · :160(搜「asymmetry between reading and writing」) · :236(搜「shadowing」)
[[Prototype]] 与链尾 同上 text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:166(搜「[[Prototype]] reference」) · :172(搜「ends at Object.prototype」)
查找伪代码 同上 text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:185(搜「prototypeChainLookup」)
访问器 setter 的落点 同上 text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:242(搜「setter on the prototype」)
原型污染攻击演示 同上 text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:286(搜「prototype pollution」) · :324(搜「isAuthorized = true」) · :319(搜「Access denied」)
options 污染变体 同上 text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:354(搜「configureDrawer(options)」) · :361(搜「Object.prototype.accessLevel」)
两种防御 同上 text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:386(搜「Object.hasOwn(options, 'label')」) · :402(搜「both ends of the call」)
'p' in obj 与 hasOwn 同上 text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:420(搜「getPrototypeOf」) · :444(搜「'prop' in obj」) · :467(搜「Object.hasOwn」)
if(obj.prop) 的误用 同上 text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:497(搜「mistakenly use」)
this 的运行时决定 5.4 The mystery of this: context and identity text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:7(搜「determined at runtime」)
三种归宿与严格模式 同上 text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:37(搜「three values」) · :61(搜「globalThis」)
「他们来了」比方 同上 text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:69(搜「They’re here」)
call(1) 的装箱 同上 text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:82(搜「coerce it into an object」)
Eich 的「错误」自评 同上 text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:117(搜「SmallTalk」)
this 与原型链合流 同上 text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:131(搜「original object」) · :161(搜「tax documents drawer」) · :173(搜「binding behavior」)
class 争议 同上 text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:216(搜「controversial」)
糖与等价展开 同上 text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:347(搜「syntactic sugar」) · :387(搜「actually doing」) · :394(搜「Object.create」)
「不只是糖」与私有字段 同上 text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:401(搜「aren’t just sugar」) · :419(搜「#pinCode」) · :457(搜「truly inaccessible」)
static 的真义 同上 text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:503(搜「separate objects」) · :530(搜「static label」)


  1. 出处:「5.3 Prototypical inheritance: the filing cabinet family tree」第 13 段(text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:13,搜「prototype’s prototype」)。 ↩

  2. 出处:「5.3 Prototypical inheritance: the filing cabinet family tree」第 44 段(text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:44,搜「not through classes or copying」)。「两条动态」的分述见第 50 段与第 84 段。 ↩

  3. 出处:「5.3 Prototypical inheritance: the filing cabinet family tree」第 50–61 段(text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:50,搜「immediately see the change」;加 close 方法后两个抽屉立刻可用的代码在 :57–:61)。「方法没有被复制」见第 71 段(搜「not being copied」)。 ↩

  4. 出处:「5.3 Prototypical inheritance: the filing cabinet family tree」第 84–106 段(text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:84,搜「Object.setPrototypeOf() API」;:98 搜「setPrototypeOf(drawer」)。 ↩

  5. 出处:「5.3 Prototypical inheritance: the filing cabinet family tree」第 116 段(text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:116,搜「bugs or security vulnerabilities」)。 ↩

  6. 出处:「5.3 Prototypical inheritance: the filing cabinet family tree」第 141–148 段(text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:141,搜「drawerPrototype = { label: 'default' }」)。 ↩

  7. 出处:「5.3 Prototypical inheritance: the filing cabinet family tree」第 236 段(text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:236,搜「shadowing」)。「不删就永久无视」见第 160 段(搜「unless the new property is later deleted」)。 ↩

  8. 出处:「5.3 Prototypical inheritance: the filing cabinet family tree」第 160 段(text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:160,搜「constant source of pain」)。 ↩

  9. 出处:「5.3 Prototypical inheritance: the filing cabinet family tree」第 249–260 段(text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:251,搜「get label()」)。 ↩

  10. 出处:「5.3 Prototypical inheritance: the filing cabinet family tree」第 274 段(text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:274,搜「knowing which case you’re dealing with」)。 ↩

  11. 出处:「5.3 Prototypical inheritance: the filing cabinet family tree」第 185–201 段(text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:185,搜「prototypeChainLookup」)。 ↩

  12. 出处:「5.3 Prototypical inheritance: the filing cabinet family tree」第 172 段(text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:172,搜「ends at Object.prototype」)。 ↩

  13. 出处:「5.3 Prototypical inheritance: the filing cabinet family tree」第 420–480 段(text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:420,搜「getPrototypeOf」;:444 搜「'prop' in obj」;:467 搜「Object.hasOwn」)。 ↩

  14. 出处:「5.3 Prototypical inheritance: the filing cabinet family tree」第 491–497 段(text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:497,搜「mistakenly use if (obj.prop)」)。 ↩

  15. 出处:「5.3 Prototypical inheritance: the filing cabinet family tree」第 286 段(text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:286,搜「prototype pollution」)。定义见第 299 段(搜「insert properties」)。 ↩

  16. 出处:「5.3 Prototypical inheritance: the filing cabinet family tree」第 314–331 段(text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:314,搜「openDrawer」;:324 搜「isAuthorized = true」;:331 搜「Opening Confidential Documents」)。 ↩

  17. 出处:「5.3 Prototypical inheritance: the filing cabinet family tree」第 307 段(text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:307,搜「Every object in the application」)。 ↩

  18. 出处:「5.3 Prototypical inheritance: the filing cabinet family tree」第 354–367 段(text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:354,搜「configureDrawer」;:361 搜「accessLevel = 'admin'」)。 ↩

  19. 出处:「5.3 Prototypical inheritance: the filing cabinet family tree」第 384–402 段(text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:386,搜「Object.hasOwn(options」;:402 搜「when in doubt, use both」)。 ↩

  20. 出处:「5.3 Prototypical inheritance: the filing cabinet family tree」第 402 段(text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:402,搜「defense in the API」)。 ↩

  21. 出处:「5.4 The mystery of this: context and identity」第 7 段(text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:7,搜「determined at runtime」)。 ↩

  22. 出处:「5.4 The mystery of this: context and identity」第 7 段(text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:7,搜「loosely coupled」)。 ↩

  23. 出处:「5.4 The mystery of this: context and identity」第 37–61 段(text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:37,搜「one of three values」;:61 搜「strict mode」与「globalThis」)。 ↩

  24. 出处:「5.4 The mystery of this: context and identity」第 69 段(text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:69,搜「They’re here」)。 ↩

  25. 出处:「5.4 The mystery of this: context and identity」第 82–96 段(text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:82,搜「coerce it into an object」;:90 搜「foo.call(1)」)。 ↩

  26. 出处:「5.4 The mystery of this: context and identity」第 117 段(text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:117,搜「SmallTalk」与「a mistake」)。「10 天」在同段。 ↩

  27. 出处:「5.4 The mystery of this: context and identity」第 131 段(text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:131,搜「original object」)。 ↩

  28. 出处:「5.4 The mystery of this: context and identity」第 144–169 段(text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:144,搜「drawerPrototype = {」;:161 与 :163 的两行输出「tax documents」/「receipts」)。 ↩

  29. 出处:「5.4 The mystery of this: context and identity」第 173 段(text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:173,搜「Without this behavior」)。 ↩

  30. 出处:「5.4 The mystery of this: context and identity」第 216 段(text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:216,搜「controversial」)。 ↩

  31. 出处:「5.4 The mystery of this: context and identity」第 347 段(text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:347,搜「abbreviated form」)。 ↩

  32. 出处:「5.4 The mystery of this: context and identity」第 387–395 段(text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:387,搜「actually doing」;:394 搜「Object.create(Drawer.prototype)」)。 ↩

  33. 出处:「5.4 The mystery of this: context and identity」第 280–337 段(text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:287,搜「class Drawer」;:330 搜「instanceof」)。extends/super 的例程见 :287–:337。 ↩

  34. 出处:「5.4 The mystery of this: context and identity」第 401 段与第 419–457 段(text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:401,搜「aren’t just sugar」;:419 搜「#pinCode」;:457 搜「truly inaccessible」)。 ↩

  35. 出处:「5.4 The mystery of this: context and identity」第 503 段与第 522–543 段(text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:503,搜「separate objects」;:530 搜「static label = 'Secure'」)。「借来的术语、在 JS 里另有所指」见第 503 段。 ↩

  36. 出处:「5.3 Prototypical inheritance: the filing cabinet family tree」第 141–160 段、第 185–201 段(text/18-ch05-03-5-3-prototypical-inheritance-the-filing-cabinet-.txt:141,搜「label: 'default'」)。 ↩

  37. 出处:「5.4 The mystery of this: context and identity」第 354–381 段(text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:354,搜「class Drawer {」)。 ↩

  38. 出处:「5.4 The mystery of this: context and identity」第 274 段(text/19-ch05-04-5-4-the-mystery-of-this-context-and-identity.txt:274,搜「solve organizational problems」)。 ↩