"this 到底指向谁?"
几乎每个前端都在控制台里抓狂过。本文带你逐条拆解 this 的四条核心规则、箭头函数特例与常见坑点,附赠记忆口诀与实战 Demo,一次性扫清疑惑。
一、this 是什么?
- 不是函数本身,不是函数词法作用域
- 是"调用者"在运行时偷偷塞进来的隐藏参数------谁把我喊出来,我就指向谁
- 因此 this 与函数声明位置无关,与调用方式有关
二、四条黄金规则(涵盖 99% 场景)
规则 触发场景 this 指向 严格模式差异
- 默认绑定 普通函数直接调用
foo()
浏览器:window
;Node:global
严格 →undefined
- 隐式绑定 对象方法调用
obj.foo()
方法所属对象 丢失场景见下文 - 显式绑定
call/apply/bind
手动指定的第一个参数 传null/undefined
会被忽略 → 回退到全局 - new 绑定
new Foo()
新创建的实例 构造器默认return this
记忆口诀:"无点默认,有点隐式,call 显式,new 实例"
三、规则详解 + 代码示例
① 默认绑定
js
function foo() {
console.log(this.a);
}
var a = 1;
foo(); // 1 (非严格)
js
'use strict';
foo(); // undefined → 不再指向 window
② 隐式绑定 & 常见的"丢失"陷阱
js
const obj = {
a: 2,
foo: foo
};
obj.foo(); // 2 → this === obj
隐式丢失三件套(面试高频):
js
// 1. 赋值
const bar = obj.foo;
bar(); // 1 → 回到默认绑定
// 2. 传参
setTimeout(obj.foo, 0); // 1 → 回调里丢失 obj
// 3. 运算符
(false || obj.foo)(); // 1
破解办法:bind
/ 箭头函数 / 包装函数
③ 显式绑定
js
const obj = { a: 3 };
foo.call(obj); // 3
foo.apply(obj, []); // 3
const bar = foo.bind(obj);
bar(); // 永远 3
bind 柯里化:
js
function add(a, b) { return a + b + this.c; }
const bound = add.bind({ c: 10 }, 5); // 固定 this 与第一个参数
console.log(bound(2)); // 5+2+10 = 17
④ new 绑定
js
function Animal(name) {
this.name = name;
}
const dog = new Animal('旺财');
console.log(dog.name); // 旺财
构造器显式返回对象会覆盖默认 this:
js
function Foo() {
this.x = 1;
return { y: 2 }; // 返回新对象 → this 被丢弃
}
console.log(new Foo()); // { y: 2 }
四、箭头函数:this 的"静态快照"
箭头函数没有自己的 this,它捕获外层函数执行上下文的 this(词法作用域)。
js
function timer() {
this.seconds = 0;
setInterval(() => {
this.seconds++; // 继承 timer 的 this
}, 1000);
}
new timer();
对比普通函数:
js
setInterval(function () {
this.seconds++; // 丢失 → window.seconds 或报错
}, 1000);
结论
- 事件回调 / 定时器想保留外部 this → 箭头函数最简洁
- 需要动态 this(如 jQuery 插件)→ 不能用箭头函数
五、常见场景速查表
代码片段 | this 指向 |
---|---|
obj.method() |
obj |
(obj.method)() |
obj(括号不改变) |
(obj.method = obj.method)() |
默认绑定 |
事件侦听器 el.addEventListener('click', handler) |
el(DOM 元素) |
内联事件 <button onclick="handler()"> |
全局(非严格) |
箭头函数 const f = () => this |
外层 this |
类方法 class A { m() {} } |
实例(需 new A().m() ) |
六、调试技巧
- 控制台打印:
js
console.log(this);
debugger; // 断点查看调用栈
- 显式绑定调试:
js
function logThis() {
console.log(this);
}
logThis.call({ id: 42 }); // 立即确认指向
- 开发环境开启严格模式:
js
'use strict'; // 让默认绑定变成 undefined,更快暴露错误
七、一句话总结(背下来)
"this 永远指向调用它的对象;没有调用者就回退到全局(或 undefined);箭头函数除外,它只看出生环境。"
八、互动练习
js
const obj = {
fn: function () {
return this;
},
arrow: () => this
};
console.log(obj.fn()); // ?
console.log(obj.arrow()); // ?
const { fn, arrow } = obj;
console.log(fn()); // ?
console.log(arrow()); // ?
答案:
obj → window → undefined → window
把这篇文章收藏起来,下次再遇到 this 疑难杂症,先问"调用点在哪",再看"四条规则",90% 的问题都能秒解。祝你早日驯服这只"变色龙"!