08 原型链、this 与 class:活的继承,和它的黑暗面
08 原型链、this 与 class:活的继承,和它的黑暗面
这一章讲什么
上一章结尾留下一个问题:drawer.toString() 这个方法,drawer 身上根本没有,
凭什么能调用?这一章回答它,并且把答案展开成 JavaScript 对象体系的三大件:
- 原型链:属性查找的完整路线——读沿链向上找,写只落在自己身上;
- 原型污染:这条链作为攻击面的经典漏洞,以及两种防御;
- 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 吗」 | 陷阱见下 |
最后一个是最常见的误用:用 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:
- 调用侧:构造 options 时显式
__proto__: null,造一个无原型的对象, 链根本不存在,污染无从落地; - 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。
作者的判断与证据
有证据的判断:
作者的主张:
- 「原型污染是经典漏洞」的定性,书中给的是教学化演示,没有引用 CVE 级真实案例; 现实中它确实常年出现在真实漏洞库里,但那是书外证据。
- 「class 是错误」的争议双方,书中如实并陈,作者自己未站队—— 只陈述它解决了组织问题(初始化与方法收拢在一处)38。
判断(我们的,不是书里的): 「读走链、写留身」的不对称是 JS 对象系统里最 值得记住的一条——「我以为改了配置,其实新建了同名属性」这一类 bug, 根源全在这一句;它比原型污染这类大词更高频。 如果错,会错在: 在只用 class、不写散对象的现代代码风格里,遮蔽的机会大幅减少, 这条不对称的实际杀伤力在下降——但它仍在(任何合并 options 对象的场景),不会归零。
边界与局限
- this 的「非典型绑定」(
call/apply/bind、箭头函数的继承绑定) 全书明确推迟到「函数一章」——MEAP 里那一章不存在,这是本书最大的未完待续。 - 原型污染的真实攻防(哪类 JSON 解析器、合并函数易中招)书中没展开, 只给了原理和两个防御动作。
class的字段初始化顺序、私有方法的 this 校验等边角,原书未涉及。Object.setPrototypeOf的性能代价(强制去优化)没有提—— 它和 10 章的内联缓存直接相关,读完 10 章要记得回来自行补上这条。
可带走的
- 原型 = 另一个对象;读沿 [[Prototype]] 链上找,找到 null 为止;链顶通常是 Object.prototype。
- 写只在自 己身上发生(数据属性),会遮蔽原型;原型是访问器时写沿链找 setter。
- 四种查属性方式(getOwnPropertyDescriptor / in / hasOwn / if(obj.p))各查各的,别拿真值性冒充存在性。
- 原型污染 = 往 Object.prototype 注属性;防御:Object.hasOwn + null 原型对象,两头都布。
- this 由调用方式决定,不由函数定义位置决定;严格模式下无人认领时是 undefined。
- 方法经原型链找到后,this 指向发起查找的对象——共享方法的生命线。
- class 是原型机制的糖:new 做的事就是 Object.create + call;但 #私有字段、static 是真新增。
- 记住 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」) |
Footnotes
-
出处:「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」)。 ↩ -
出处:「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 段。 ↩ -
出处:「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」)。 ↩ -
出处:「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.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」)。 ↩ -
出处:「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' }」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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()」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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)」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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'」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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)」)。 ↩ -
出处:「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 天」在同段。 ↩ -
出处:「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」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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」)。 ↩ -
出处:「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)」)。 ↩ -
出处:「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。 ↩ -
出处:「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」)。 ↩ -
出处:「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 段。 ↩ -
出处:「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'」)。 ↩ -
出处:「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 {」)。 ↩ -
出处:「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」)。 ↩