02this 与绑定
this 是 JS 里最「看脸色」的关键字——同样一行 this.name,
在不同调用方式下可能指向完全不同的对象。本课把四条绑定规则、call/apply/bind、
箭头函数的例外一次讲透。
一、为什么需要 this
写一个「打印用户名」的函数时,不想每次把对象当参数传来传去,
就用 this 指代「当前操作的对象」。问题是:
this 到底指向谁,由「怎么调用」决定,不由「写在哪」决定。
二、四条绑定规则(优先级从低到高)
1默认绑定
独立调用 fn():非严格模式 this = window;严格模式 = undefined。
2隐式绑定
作为对象方法调用 obj.fn():this = obj。看「.」前面是谁。
3显式绑定
call / apply / bind 强制指定 this,优先级高于隐式。
4new 绑定
new Fn() 时 this = 新建的实例对象,优先级最高。
点按钮看 this 指向
三、call / apply / bind 三件套
| 方法 | 参数 | 是否立刻调用 | 返回 |
|---|---|---|---|
| fn.call(thisArg, a, b) | 参数逐个传 | ✓ 立刻执行 | 函数返回值 |
| fn.apply(thisArg, [a,b]) | 参数装数组 | ✓ 立刻执行 | 函数返回值 |
| fn.bind(thisArg, a) | 可预填参数 | ✗ 返回新函数 | 绑好 this 的新函数 |
点按钮看结果
四、箭头函数:没有自己的 this
箭头函数不绑定 this,它会沿作用域链向外找「定义时」外层的 this。 这一条规则解决了「回调里 this 丢了」的 90% 问题。
| 普通函数 | 箭头函数 | |
|---|---|---|
| this | 调用时决定(四规则) | 定义时外层 this(词法) |
| arguments | 有 | 无(用 ...args) |
| new | 可以 | 不可以当构造函数 |
| prototype | 有 | 无 |
| 适用 | 方法、构造函数、需要动态 this | 回调、map/filter、简短逻辑 |
点按钮对比
this = 按钮),
用普通函数或事件里的 event.currentTarget,不要用箭头。五、常见踩坑对照
| 写法 | this 指向 | 坑 |
|---|---|---|
obj.fn() | obj | — |
const f = obj.fn; f() | window / undefined | 方法被摘出来 |
obj.fn.call(other) | other | — |
setTimeout(obj.fn, 0) | window / undefined | 传的是函数引用 |
setTimeout(() => obj.fn(), 0) | obj | 正确:包一层 |
new Fn() | 新实例 | — |
btn.addEventListener('click', fn) | btn | fn 须是普通函数 |
btn.addEventListener('click', () => …) | 外层 | 箭头拿不到按钮 |
六、严格模式下的 this
'use strict' 下,独立调用的函数 this 是 undefined 而不是 window。
ES Module 默认就是严格模式——所以模块里的 fn() this = undefined。
七、和闭包 / 原型的连接
- 闭包保存的是变量;bind 保存的是 this——两者常一起用。
- 原型方法里的 this 依赖调用方式:
arr.map(fn)时 fn 的 this 默认 undefined。 - 写
class时,方法里的 this 依赖实例调用;传作回调要.bind(this)或改箭头。
八、本章小结
- this 由调用方式决定,不是由定义位置决定(箭头函数除外)。
- 四规则优先级:new > 显式(call/apply/bind)> 隐式(obj.fn)> 默认。
call/apply立刻调用;bind返回新函数、可预填参数。- 箭头函数没有自己的 this,沿定义处外层取——回调首选。
- 方法被摘出来单独调用会丢 this;
setTimeout(obj.fn)是经典 bug。 - 严格模式 / 模块中独立调用 this =
undefined。
bind 把一个「获取用户名」方法绑到指定对象后再当回调传给 setTimeout;
② 写一个 Person 构造函数,用 new 创建两个实例并证明 this 各自独立;
③ 把一个用了 arguments 的普通函数改写成箭头函数 + 剩余参数。