this 与四种调用 — 函数上下文的来历、失控与收场
this 与四种调用 — 函数上下文的来历、失控与收场
这一章讲一件事,但讲透:
this这个关键字。它常被误传为「指向调用它的对象」, 真相更奇怪——它是函数的一个隐式参数,值由调用方式现场决定。 本章把四种调用方式逐个拆开,看this分别是什么;再看「解绑方法」这一类经典事故; 最后看两种收场手段:bind和箭头函数。 读完你会明白 JavaScript 最著名的那句抱怨:「我只是把方法存进了变量,它怎么就报错了?」
1. this:一个看不见的参数
每调用一个函数,除了你写在括号里的实参,JavaScript 还悄悄塞进去一个参数,叫 this。
它指向「与本次调用相关联的对象」,所以有个别名:函数上下文(function context)。1
来自 Java/C# 的开发者觉得自己懂 this:在类的方法里,它不就是「我这个实例」吗?
现代 JavaScript 里最常见的用法确实如此——书里的 Ninja 类,this.#skillLevel++
里的 this 就是那个练功的忍者实例。2
但原书马上按住了这个理解。this 在 JavaScript 里不是由函数定义的位置决定的,也不总是实例——
它可以受调用方式影响。这一章的全部内容,就是这个「可以」到底怎么发生。
主线例子:一个计数器对象
为了让每个机制都有落点,全章用同一个场景走到底:
页面上有个按钮,一个对象 buttonObject 负责记点击次数,处理器是它的方法 clickHandler。
问题只有一个:把 clickHandler 交给浏览器时,this 还是不是 buttonObject?
场景:buttonElement.addEventListener("click", ???)
│
├─ ??? = buttonObject.clickHandler ← 直接传:翻车(§3)
├─ ??? = buttonObject.clickHandler.bind(buttonObject) ← 绑死:修复①(§4)
├─ ??? = () => buttonObject.clickHandler() ← 包一层:修复②(§4)
└─ 结论:连「存进变量」这个无害动作都会改变 this(§1.1)1.1 经典事故:存进变量,行为变了
先看事故本身(书里的 Ninja 版):3
const ninja = new Ninja();
const trainNinja = ninja.train; // 只是把它存进一个变量……
trainNinja(); // 运行时报错:this 是 undefinedtrain 方法内部有一句 this.#skillLevel++。直接调 ninja.train() 一切正常;
可一旦先存进 trainNinja 再调,this 变成 undefined,句点属性访问立刻抛错。
原书特意强调:「重新赋值」本身不是病根。 看这个变体:4
const ninja = new Ninja();
const trainCopy = ninja.train;
ninja.trainCopy = trainCopy; // 同一个函数,挂回 ninja 名下
ninja.trainCopy(); // 正常!函数一个字没变,只是换了个挂的地方(此例绕过了 TypeScript 的类型检查,纯 JavaScript 合法)。
差别在哪?调用那行的写法。ninja.trainCopy() 是方法调用,trainNinja() 是普通调用——
调用方式不同,this 就不同。这就是全章的总纲,下一节把四种方式钉死。
(工具提示:typescript-eslint 的 unbound-method 规则能静态抓出「把方法赋给变量」这类写法。5)
2. 四种调用方式:this 的四种来源
原书给的分类,完整且互斥——任何一个非箭头函数的调用,必属其一:6
| 调用方式 | 写法 | this 是什么 |
|---|---|---|
| 普通函数调用 | skulk() | undefined(严格模式;非严格是全局对象) |
| 方法调用 | ninja.skulk() | 点号前面的那个对象 ninja |
| 构造器调用 | new Ninja() | 一个刚创建的空对象 |
| call/apply 调用 | skulk.call(ninja) | 第一个实参:ninja |
逐个走。
2.1 普通调用:孤零零的 ()
函数不作为方法、不陪 new、不走 call/apply,就是普通调用。ninja()、samurai()、
(function(){})()(IIFE 也算)都在此类。7
严格模式下 this 是 undefined。这是 ES5(2009)「strict mode(严格模式)」
修正的历史事故之一:老行为里,普通函数里的 this 是全局对象——
于是 this.someProperty 静默访问到 undefined 的全局属性,程序莫名其妙地坏;
改成 undefined 后,这种错误当场炸、当场可见。原书称之为「fail in a clear, immediate way」,
并给出背景:TypeScript、Babel 默认都吐 "use strict",ESM 模块天然就是严格模式,
所以「this 是 undefined」才是现代代码的常态。8
两个例外(书内列出的):函数若被 bind 过,this 以绑定为准(§4);非严格模式下还是全局对象。
2.2 方法调用:点号前面是谁,this 就是谁
函数挂在对象的属性上、且通过「属性名 + 括号」调用,就是方法调用:
const ninja1 = { getMyThis: whatsMyContext };
ninja1.getMyThis(); // this === ninja1实验(原书 Listing 6.2)干净地展示了同一函数的两面:
"use strict";
function whatsMyContext() { return this; }
whatsMyContext() === undefined // 普通调用:this 消失
const ninja1 = { getMyThis: whatsMyContext };
ninja1.getMyThis() === ninja1 // 方法调用:this 是挂着的对象同一个函数,前一秒 undefined,后一秒是 ninja1。this 是调用现场装配的,不是出厂自带的。9
两个陷阱(原书专门列出):10
- 箭头函数和 bind 过的函数不适用这条规则——它们有自己的规则(§4);
- 只有点号写法才算。
ninja1["getMyThis"]()这种方括号写法,在书里被划成「行为如同普通调用」 的一类——它拿不到挂载对象作 this。(此处原书的表述比规范严格,工程上按「方法调用的识别以 调用表达式的形态为准」理解即可;日常代码中点号是绝对主流。)
2.3 构造器调用:new 的魔法三步
任何用 function 声明或表达式定义的函数(箭头函数、bind 产物除外),前面加 new 就变成了构造器。
new Ninja() 触发的是一套固定动作,书里画成了三步(我们照抄):11
1. 造一个空对象 {}
2. 把它作为 this 塞给函数
3. 函数体往 this 上挂东西;结束时,除非函数显式 return 了另一个对象,否则返回这个 this用 Ninja 构造器带数走一遍(书里的例子):12
function Ninja() {
this.skulk = function () { return this; }; // 给刚造出的空对象挂一个方法
}
const ninja1 = new Ninja();
const ninja2 = new Ninja();
ninja1.skulk() === ninja1 // ✓ 每个实例的 skulk,指回自己
ninja2.skulk() === ninja2 // ✓ 两个实例互不相干第 1 次 new Ninja():空对象造出 → this 指向它 → 挂 skulk → 返回它,ninja1 拿到这个对象。
第 2 次同理,ninja2 是另一个对象。skulk 里的 this 永远指回「造出自己的那次调用」的对象。
一个反直觉细节(书里单独立框):构造器里显式 return 的行为很怪——13
- return 一个对象:返回它,丢掉 this(构造出的对象被弃);
- return 一个原始值(数字、字符串):return 被无视,照常返回 this。
工程结论:构造器里别写 return。
另外两件实用小物:new.target(ES2015)能可靠分辨「我是不是被 new 调的」
(new.target !== undefined);老库时代曾流行「加不加 new 都行」的函数(如 Object()),
如今被 TypeScript 和 ESLint 视为该退烧的怪癖——现代约定:构造器写进 class,类名大写。14
2.4 call 与 apply:调用方直接指定 this
前三种方式都是「隐式」决定 this;最后一种是显式的:每个函数自带 call 和 apply 两个方法
(函数是一等对象,当然能挂方法),第一个参数就是「本次调用的 this」,之后的参数照常:15
function juggle(...numbers) {
let result = 0;
for (const number of numbers) result += number;
this.result = result; // 往「上下文对象」上写结果
}
const ninja1 = { result: 0 };
const ninja2 = { result: 0 };
juggle.apply(ninja1, [1, 2, 3, 4]); // this=ninja1,实参来自数组 → ninja1.result === 10
juggle.call(ninja2, 5, 6, 7, 8); // this=ninja2,实参逐个列 → ninja2.result === 26唯一差别:apply 的实参打一个数组给,call 的实参逐个列。带数走一遍:
juggle.apply(ninja1, [1,2,3,4]) 收到 this=ninja1、numbers=[1,2,3,4],求和 10,写进 ninja1.result;
juggle.call(ninja2, 5,6,7,8) 求和 26,写进 ninja2。16
历史注脚:apply 当年有一个绝技:把数组「摊开」当实参列表用(Math.max.apply(null, [3,7,2]))。
今天这个功能归 展开运算符(spread):Math.max(...numbers)。加上 bind 的普及,
call/apply 已不如当年常见——但「从别的对象借一个方法来用」时,它们仍是正解。17
3. 解绑方法事故:回到那个计数器
现在把 §1.1 的原理真正讲穿。主线例子的「翻车版」:18
const buttonObject = {
clickCount: 0,
clickHandler: function () {
this.clickCount++;
appendToEventList(`Click count: ${this.clickCount}`);
},
};
buttonElement.addEventListener("click", buttonObject.clickHandler); // 解绑的方法点下按钮,页面打印 Click count: NaN,而且每次都 NaN。机制链:
addEventListener 收到的,只是 clickHandler 这个「函数值」
│ (传参只传值——函数是值,值只有一份,不带「我从哪来」的标签)
▼
点击发生,浏览器以「普通函数」方式回调它:this = undefined(严格模式)
│
▼
undefined++ → NaN;模板串里 NaN 上卡着——于是 "Click count: NaN"原书把 buttonObject.clickHandler 这种「脱离了原对象、还指望 this 管用的」函数叫
unbound method(未绑定方法):「你把方法传给一个函数,那个函数只拿到方法本身,
没有任何途径知道它曾挂在哪个对象上。」19
修复方案有两个,正好是这一章的两种「收场」。
4. 两种收场:bind 与箭头函数
4.1 bind:造一个 this 钉死的新函数
每个函数还有第三个方法 bind。它和 call 的关键区别:不调用,而是造一个新函数——
新函数体与原函数相同,但 this 永远是被绑定的那个对象,无论它之后怎么被调用。20
buttonElement.addEventListener(
"click",
buttonObject.clickHandler.bind(buttonObject) // 修复:点击打印 1、2、3……
);点击三次,输出 1、2、3——bound 函数里 this.clickCount++ 永远改的是 buttonObject 的。
两个要点:21
- bind 不动原函数。每次调用
bind都产出一个新函数——以为「bind 是在给原函数上锁」是常见误会; - bind 过的函数,再方法调用、再 call/apply 也改不了它的 this(规则冲突时绑定赢,普通调用那次除外…… 其实普通调用时绑定的 this 照样生效——绑定对一切调用方式生效,这正是它的意义)。
bind 曾风行到一种程度:构造器里把每个方法都替换成绑定副本。ES2022 的类字段(class field)
语法让这件事有了原生写法(注意是 = 不是 :):22
class Button {
#clickCount = 0;
clickHandler = () => { // 类字段:每个实例一份,绑死在自己实例上
this.#clickCount++;
appendToEventList(`Click count: ${this.clickCount}`);
};
}类字段的 clickHandler 技术上是一种特殊的箭头函数——这就接到下一种收场。
4.2 箭头函数:连「自己的 this」都不要
箭头函数对 this 问题的回答是釜底抽薪:它没有自己的 this。
函数体内的 this,就是它定义处外层的那个 this——术语叫词法上下文(lexical context)。23
在 new 的三步里看最直接。改造前的老姿势(箭头函数之前,开发者手工模拟词法 this):
function Ninja() {
const _this = this; // 抓住构造时的 this,存进普通变量
this.skulk = function () { return _this; }; // 方法永远用 _this,免疫调用方式
}箭头函数干的就是这件事,而且是语法保证:
function Ninja() {
this.skulk = () => { return this; }; // this = 定义处外层 = 构造时的实例,板上钉钉
}两条保证(原书明列,都是「免疫」级的):24
- 用
call/apply调箭头函数?this 不受影响; - 对箭头函数
bind?新函数的 this 和原函数一样(绑不动)。
TypeScript 也从中受益:它能可靠推断箭头函数里 this 的类型(定义处定死了),
普通函数则不行——这是工程上偏爱箭头函数的一个隐性理由。25
但有一个著名的坑,原书单独警告:对象字面量里别这么写。26
const buttonObject = {
clickCount: 0,
clickHandler: () => { // 别这么干!
this.clickCount++; // this 是「定义处外层」的 this——也就是外层的函数(或模块)的 this
...
},
};方法定义在对象字面量里,而普通对象不产生词法上下文——只有函数(以及 ES2022 起,类)产生。
所以这里的 this 抓的是外层那个函数的 this,跟 buttonObject 毫无关系。
普通对象上没有定义「绑定方法」的原生途径。
这个坑的正确绕法,也正是主线例子的第二修复:包一层。
buttonElement.addEventListener("click", () => { buttonObject.clickHandler(); });箭头函数在这里的价值不是 this(它根本没用到外层的 this),
而是**「把一个调用原样转交」**的简洁——想稳妥传参,用 rest 参数透传:
(...args) => { buttonObject.clickHandler(...args); }。27
5. 一张总表收尾
四种调用 × 箭头函数、绑定函数,合起来是本章的全部规则:28
| 函数种类 | 普通调用 | 方法调用 | 构造器调用 | call/apply |
|---|---|---|---|---|
| 普通函数(function 声明/表达式) | undefined(严格) | 挂着的对象 | 新对象 | 第一参数 |
bind 产物 | 绑定的对象 | 绑定的对象 | (不用作构造器) | 绑定的对象 |
| 箭头函数 | 定义处外层的 this | 同上 | 不能当构造器 | 同上(call/apply 改不动) |
| 类字段 | 所在实例 | 所在实例 | — | 所在实例 |
记不住的读者只需记一句:「this 看调用」只对 function 函数成立;箭头函数看定义,bind 看绑定。
6. 作者的判断与证据
可亲手验证的机制(原书全部给了 assert):
- 同一函数在普通/方法调用下
this不同(Listing 6.2 的两个 assert); new三步中「return 对象覆盖 this、return 原始值被无视」的行为;- bind 产物 this 不受调用方式影响;箭头函数 this 不受 call/apply/bind 影响。29
作者立场(当立场读):
- 「构造器写进 class、类名大写、普通函数别 new」被说成现代约定——是社区规范而非语言强制;
- 「React 式事件处理器该用类字段或包箭头」是作者推荐的修复姿势,等价方案很多;
- 非严格模式下 this=全局对象,被定性为「language mistakes」之一——这是工程共识,但规范层面它是被冻结的历史行为,不会改了。
7. 边界与局限
- 方括号调用那条(ninja1"getMyThis" 行为如普通调用)是原书的实验性表述,与规范文本存在出入; 读者只需掌握「点号是方法调用的识别特征」这一安全口径,异常案例留给规范。
- bind 的部分应用(只绑 this、后面的实参也预填:fn.bind(obj, 1))本章未展开,原书没有覆盖。
- 类字段为何是「特殊箭头函数」(实例字段 vs 原型方法的内存代价)要等第 9 章。
- 事件处理器里
this还会受浏览器「回调时指定 this」的老行为影响(HTML 属性式 onclick), 原书只讲了 addEventListener 的正统路径。
8. 可带走的(合上这一章,你应该有这八条)
- this 是隐式参数,值由调用方式现场决定——「指向调用它的对象」只对方法调用成立。
- 四种调用四种 this:普通调用 undefined(严格)、方法调用是点号前的对象、构造器调用是新建空对象、 call/apply 是第一参数。
- 存方法进变量 = 解绑;传参只传值,函数值不携带「我从哪来」。
new的三步:造空对象 → 当 this 塞入 → 返回它(return 对象会顶替、return 原始值被无视)。- call 逐个列实参,apply 打一个数组;数组摊开的需求如今归展开运算符
...。 - bind 每次造新函数、不动原函数,绑定的 this 对一切调用方式生效。
- 箭头函数没有自己的 this:取定义处外层的 this,call/apply/bind 都改不动;不能当构造器。
- 普通对象字面量不产生词法上下文——对象里写箭头函数方法是个坑;想转交调用,包一层箭头函数。
10. 深读一:this 机制的设计考古,与四条规则的边界
10.1 「隐式参数」设计的历史账
书里在 strict mode 一节给了一段考古,值得单独成段:ES5(2009)之前,
普通函数里的 this 是全局对象——于是 this.someProperty 默默读到的是
全局上的同名属性(undefined),程序坏得无声无息。strict mode 把它改成
undefined,错误当场抛、位置当场可见。原书称这是「fail in a clear, immediate way」,
并补了一句现实:这个「错误」永远不会被规范改回去了——非严格行为已被冻结,
能改的是「让默认成为严格」:TypeScript 和 Babel 默认吐 "use strict",
ESM 模块天然严格,所以今天的新代码几乎活在严格世界里。1
由此得到本章规则的一个更精确版本:
「this 看调用」这句话,只在非箭头、非绑定的 function 函数上成立; 箭头函数看定义,bind 产物看绑定。三种函数、三套规则,混用前先问「它是哪种函数」。2
10.2 方法调用的「点号识别」,一个值得会心的细节
书里的方法调用陷阱(方括号写法)揭示了 JavaScript 解析的一个基本面:
方法调用的身份由「调用表达式的形态」决定,不由函数挂在哪个对象上决定。
ninja1.getMyThis() 和 ninja1["getMyThis"]() 在执行语义上是一回事,
原书把后者列为「行为如同普通调用」的边界案例,是为了强调「点号识别」的教学点。
日常代码里点号是绝对主流,这个细节知道即可;真正要刻进肌肉的是:
换调用形态(存变量、传参数、包一层)之前,先想 this 会不会跟着变。3
11. 深读二:构造器机制,从「普通函数」到「类」的完整演化
11.1 「任何函数都能 new」的来龙去脉
书里有一个 JavaScript 独有的怪癖值得正视:凡 function 声明/表达式,前面都能加 new——
哪怕那个函数根本没打算当构造器。new whatsMyContext() 造出一个对象,
它的原型是 whatsMyContext.prototype,然后被丢弃。这带来两个历史现象:4
- 老库的「可加可不加 new」设计:
Object()不加 new 和加 new 等价; 库为了迁就用户,让函数内部自己判断「我是不是被 new 调的」; - ES2015 的
new.target:可靠的自判手段——new.target !== undefined即为构造调用。 书里的评价很干脆:这个机制存在,但你应该用 class, 让「我是不是构造器」这个问题干脆不存在。
11.2 构造器里的 return,两条怪规则的机制解释
「return 对象则顶替 this、return 原始值被无视」为什么长成这样?机制上说得通:
new 的语义是「构造并返回一个对象」,如果函数体 return 了一个对象,
那它就是这个操作的结果——语义自洽;return 一个数字则不是「对象」,
与 new 的返回类型承诺冲突,于是被无视、回落到 this。
但「说得通」不等于「该用」:原书的建议直白——构造器里别写 return;
非写不可时,用 TypeScript 的 construct signature 告诉检查器你的意图。5
12. 深读三:bind 与箭头函数的对照使用学
12.1 bind 的三个易错点
bind 用起来简单,坑在三个细节上(书里占了两个,补一个使用学):6
- bind 不动原函数——每次都产新函数。
fn.bind(o) === fn是 false; 指望「绑定原函数」是常见误会,忘了接返回值等于没绑; - 绑定是终身的——bind 产物的 this 对一切调用方式免疫(普通调、方法调、call/apply), 这正是它的用途,也是它的代价:你放弃了让调用方指定 this 的灵活性;
- 类字段是 bind 的原生替代——ES2022 的
clickHandler = () => {...}在语义上等价于「构造时给每个实例绑一份箭头函数」,区别只在写法。 书里注了一句「technically a special kind of arrow function」—— 类字段与箭头函数共享同一套 this 规则。
12.2 箭头函数的两个「免疫」与一个大坑,再确认
两个免疫(书里明列):call/apply 改不动它的 this;bind 也绑不动
(产物和原函数的 this 一模一样)。一个大坑(书里单列警告):对象字面量不产生词法上下文——
只有函数(以及 ES2022 起的类)产生。所以对象里写 clickHandler: () => { this.clickCount++ },
this 抓的是外层的 this(模块里是 undefined),跟这个对象毫无关系。7
「包一层」修复之所以推荐,是因为它同时满足三件事:
箭头函数捕获定义处的 this(它本来就不需要)、把参数原样转交
(用 (...args) 透传防漏)、以及不改动原方法的 this 语义
(方法还是方法,每次以 buttonObject.clickHandler() 的方法形态调用)。8
13. 深读四:一张决策表收尾
遇到 this 问题时的诊断流程:
这个函数的 this 不对?
│
├─ 它是箭头函数/类字段? ──是──► 去它的定义处外层找 this,调用方式无关
├─ 它是 bind 产物? ──是──► this 恒为绑定的对象
└─ 普通 function? ──► 看调用形态:
点号调用 → this = 点号前的对象
new 调用 → this = 新建的空对象
call/apply → this = 第一参数
裸调用 → this = undefined(严格)四行之外的全部怪现象(事件回调里 this 变成按钮、setTimeout 里 this 变全局), 都是「你以为它是方法调用、其实是裸调用」的变体。学会这一个诊断流程, JavaScript 最高产的 this bug 家族就对你失业了。9
14. 原文地图
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| this 作为隐式参数/函数上下文 | Chapter 6: Functions for the journeyman: understanding function invocation | text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:45(搜「implicit parameter」) |
| Ninja 类 this 的正常用法 | 同上 | :53(搜「Listing 6.1」) |
| 存进变量翻车 | 同上 | :87(搜「trainNinja」) |
| 「赋值不是病根」变体 | 同上 | :105(搜「the reassignment isn't the problem」) |
| 四种调用清单 | 同上 | :118(搜「four ways」) |
| 普通调用的 this 与严格模式 | 同上 | :160(搜「standalone function」) · :187(搜「strict mode」) |
| 方法调用实验 | 同上 | :201(搜「Listing 6.2」) |
| 方法调用两陷阱 | 同上 | :237(搜「traps with method invocation」) |
| 构造器三步 | 同上 | :285(搜「SUPERPOWERS OF CONSTRUCTORS」) · :316(搜「special actions」) |
| return 覆盖规则 | 同上 | :330(搜「USING EXPLICIT RETURNS」) |
| new.target 与现代约定 | 同上 | :350(搜「CHECKING IF A FUNCTION WAS CALLED AS A CONSTRUCTOR」) |
| call/apply 与 juggle | 同上 | :380(搜「apply and call methods」) · :395(搜「Listing 6.4」) |
| apply 的展开旧用途 | 同上 | :428(搜「spread operator」) |
| 按钮 NaN 事故 | 同上 | :452(搜「Listing 6.5」) · :498(搜「unbound method」) |
| bind 修复 | 同上 | :510(搜「Using the bind method」) |
| 类字段语法 | 同上 | :536(搜「ES2022 introduced」) |
| 词法 this 与 _this 前身 | 同上 | :554(搜「lexical context」) · :566(搜「_this」) |
| 箭头函数免疫清单 | 同上 | :587(搜「won't have any effect」) |
| 对象字面量的坑 | 同上 | :595(搜「ordinary objects don't create a lexical context」) |
| 包一层修复 | 同上 | text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:609(搜「wrap the entire」) |
| 全章总结 | 同上 | :629(搜「Summary」) |
Footnotes
-
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 43–51 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:45,搜「implicit parameter」)。this 作为隐式参数与「function context」术语来源。 ↩ ↩2 -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 53–85 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:53,搜「Listing 6.1」)。类方法中 this 的「标准」用法与随后的警告(第 81–84 段:定义位置不决定 this)。 ↩ ↩2 -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 87–103 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:104,搜「assign a function to a variable」)。存变量翻车;「Error!」是原书代码注释。 ↩ ↩2 -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 105–116 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:105,搜「the reassignment isn't the problem」)。挂回对象即恢复的反例,「The difference is how it's invoked」。 ↩ ↩2 -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 99–101 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:99,搜「unbound-method」)。typescript-eslint 规则提示。 ↩ ↩2 -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 118–126 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:121,搜「four ways」)。四种调用方式的原文清单。 ↩ ↩2 -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 160–175 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:162,搜「invoked as a standalone function」)。普通函数调用的定义与三个例子。 ↩ ↩2 -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 176–200 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:187,搜「strict mode」)。严格模式修正历史事故的来历,与 TypeScript/Babel/ESM 默认严格的现状。 ↩ ↩2 -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 201–235 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:230,搜「Working with 1st ninja」)。Listing 6.2 两个 assert 与「A function that references this can be attached to any object」。 ↩ ↩2 -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 237–245 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:239,搜「Only functions defined with the function declaration」)。方法调用的两条陷阱(箭头/bind 例外;点号写法)。 ↩ -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 285–328 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:316,搜「A new empty object is created」)。new 三步与 Figure 6.1。 ↩ -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 285–304 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:293,搜「const ninja1 = new Ninja(); #B」)。Ninja 构造器与两个 ninja 实例的 assert。 ↩ -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 330–343 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:335,搜「anything other than a primitive」)。return 对象/原始值的两种命运与「best to avoid explicit returns」。 ↩ -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 350–377 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:362,搜「new.target」)。new.target 判定;「avoid the new keyword」的现代约定见第 375–376 段。 ↩ -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 380–393 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:386,搜「Yes, we said methods of functions. As first-class」)。call/apply 的定义与第一参数语义。 ↩ -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 395–419 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:395,搜「Listing 6.4」)。juggle 带数例子:10 与 26 两个 assert 原文。 ↩ -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 428–445 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:434,搜「spread operator」)。apply 的旧绝技与展开运算符接管;「borrow a method」的遗留用途见第 443–444 段。 ↩ -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 452–509 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:455,搜「Listing 6.5」)。按钮事故完整代码与 NaN 输出(第 501–503 段)。 ↩ -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 505–508 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:506,搜「unbound method」)。「It has no way of knowing what object it was attached to」——解绑方法定义的原文。 ↩ -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 510–534 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:519,搜「bind(buttonObject)」)。bind 的修复代码与「regardless of the way we invoke it」。 ↩ -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 533–549 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:533,搜「creates a new function every time」)。「bind 每次造新函数、常见误会」的原文强调。 ↩ -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 535–552 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:540,搜「class Button」)。类字段语法与「special kind of arrow function」的定性(第 550–551 段)。 ↩ -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 554–564 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:560,搜「lexical context」)。词法上下文的定义与箭头函数「没有自己的 this」。 ↩ -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 585–593 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:586,搜「won't have any effect」)。call/apply 与 bind 对箭头函数无效的两条保证。 ↩ -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 593–594 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:593,搜「This feature of arrow functions is quite powerful」)。TypeScript 可推断箭头函数 this 类型的隐性好处。 ↩ -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 595–608 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:595,搜「ordinary objects don't create a lexical context」)。对象字面量箭头函数陷阱与「no way to directly define a bound method on a normal JavaScript object」。 ↩ -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 609–626 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:609,搜「wrap the entire」)。包一层修复与 rest 参数透传建议(第 622–624 段)。 ↩ -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 629–657 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:645,搜「influences the value」)。全章总结里四种调用与 this 对应关系的原文表述。 ↩ -
出处:「Chapter 6: Functions for the journeyman: understanding function invocation」第 296–303 段(
text/09-ch06-chapter-6-functions-for-the-journeyman-understan.txt:297,搜「The 1st ninja is skulking」);与:587(搜「won't have any effect」)。全部 assert 可复现。 ↩