结论:**大方向完全正确**——用“我”作比喻,把四条绑定规则 + 箭头函数拆开解释,是目前最容易让初学者秒懂的讲法。下面给出几点**技术层面的补充 / 修正 / 角落案例**,帮助你把这套说辞打磨到“面试官挑不出毛病”。
---
### 1. “默认绑定” 的两个细节
1. **严格模式**
```js
"use strict";
function foo() { console.log(this); }
foo(); // => undefined(不是 window)
```
把这点单独强调一下,能避免同学们在 ESLint 默认 `"use strict"` 的文件里以为自己学错了。
2. **ES Module 顶层**
在 `<script type="module">` 或 `.mjs` 文件的最外层,`this` 直接就是 `undefined`,因为 ES Module 默认是严格模式 + scope 封闭:
```js
console.log(this); // undefined
```
---
### 2. `.bind()` 的“不可逆”与优先级
你用“导演把剧本钉给演员”的比喻形象地说明了 `.bind()` 会返回**新函数**。可以再补一句:
*一旦钉好,后来再用 `.call()` / `.apply()` 都改不了了。*
```js
function foo() { console.log(this.name); }
const obj1 = {name: "A"};
const obj2 = {name: "B"};
const bound = foo.bind(obj1);
bound.call(obj2); // 依旧输出 "A"
```
所以真正的优先级是
`new > bind > call/apply > 隐式 > 默认`
(`new` 最狠,因为即便是 **已 bind 的函数**,在 `new` 时 `this` 仍是新对象;同时 `.bind()` 也能“盖过”隐式)
---
### 3. 箭头函数再加两条防坑线
1. **不能做构造器**
```js
const Foo = () => {};
new Foo(); // TypeError: Foo is not a constructor
```
2. **`call/apply/bind` 对它无效** – 既然它没有自己的 `this`,想改也改不了:
```js
const arrow = () => console.log(this);
arrow.call({x:1}); // 仍是外层的 this,而不是 {x:1}
```
---
### 4. DOM 事件处理里的 `this`
浏览器里再补充一条常用规则:
* 传统写法 `element.onclick = function(){ ... }` —— `this` 指触发事件的元素。
* 若用 **箭头函数** 绑定事件,`this` 就不是元素而是外层作用域;因此很多教程推荐:
```js
element.addEventListener("click", (e) => {
// 用 e.currentTarget 取元素,不要用 this
});
```
---
### 5. Class 语法糖带来的假象
ES6 `class` 其实只是把构造函数 + 原型链包了糖衣,底层仍然走 **new 绑定 / 隐式绑定** 逻辑。提一句可以防止“class 有特殊 this 规则”的误解。
```js
class Person {
constructor(name) { this.name = name; }
say() { console.log(this.name); }
}
```
上例中的 `say()` 跟
```js
Person.prototype.say = function(){ ... }
```
完全一致。
---
### 6. 三个面试级冷门点(了解即可)
1. **`
Function.prototype.call.bind(fn)` 用法**
高阶函数题常出现,但理解了“bind 后再 call 无法改 this”也就通透了。
2. **`super` 调用里的 `this`**
`super.method()` 内部的 `this` 仍指向调用者,而不是 `super` 指向的父类原型。
3. **`new.target`**
判断函数是否通过 `new` 调用的元信息:
```js
function Foo(){
if (!new.target) return new Foo(); // 强制 new
}
```
---
## 终极速记卡(面试背诵版)
```
① new this = 新对象
② bind this = 显式指定(永久)
③ call / apply this = 显式指定(临时)
④ obj.fn() this = obj
⑤ foo() 非严格:window;严格:undefined
★ 箭头函数 没有自己的 this,取词法外层
```
掌握这些边角后,你这份“谁在说话”类比就从**通俗易懂**升级为**覆盖 99% 场景**。剩下 1% 的诡异写法(`with`、`eval`、`Proxy` 里 trap 的 `this`)放在 OJ 里基本碰不到,知道它们存在就够了。
祝讲解更上一层楼。
