“this 不是你以为的 this,this 是 JavaScript 给你挖的坑。”
—— 一位被 this 虐哭过的前端工程师
开场白:this 的“人格分裂”现场
在 JavaScript 的世界里,有一个关键字堪称“情感大师”——它时而深情款款指向全局对象,时而冷酷无情绑定到某个函数,甚至还能在箭头函数里直接“消失”。没错,它就是 this。
很多初学者(甚至老手)一看到 this 就头皮发麻。为什么?因为它不讲武德!它的值不是定义时决定的,而是运行时由调用方式决定的。今天我们就来揭开 this 的神秘面纱,顺便聊聊怎么把它“驯服”。
一、this 的四大基本法则(别再背错了!)
1️⃣ 默认绑定:裸奔的 this → 指向全局(或 undefined)
function sayHi() {
console.log(this); // 浏览器中是 window;严格模式下是 undefined
}
sayHi(); // 裸调用!this 无依无靠
📌 记住:谁调用?没人调!那 this 就去流浪(全局)或者自闭(严格模式)。
2️⃣ 隐式绑定:对象.方法 → this 指向对象
const user = {
name: "Cherry",
greet() {
console.log(this.name); // "Cherry"
}
};
user.greet(); // 谁调用?user!所以 this === user
✅ 这是最常见也最“靠谱”的情况。但小心“隐式丢失”!
const fn = user.greet;
fn(); // this 变成 window / undefined!因为现在是裸调用了
3️⃣ 显式绑定:call / apply / bind → 手动指定 this
user.greet.call({ name: "Alice" }); // "Alice"
const boundGreet = user.greet.bind({ name: "Bob" });
boundGreet(); // "Bob"
call/apply:立即执行,指定 thisbind:返回一个“婚约函数”,this 被永久锁定(像签了忠诚协议)
💍 bind 就像给函数办了结婚证:this 此生只属于你,永不背叛。
4️⃣ new 绑定:构造函数 → this 指向新对象
function Person(name) {
this.name = name;
}
const p = new Person("David");
console.log(p.name); // "David"
🧬
new会创建一个全新对象,并把 this 绑定到它身上。这是唯一能“造人”的方式(误)。
二、定时器里的 this 陷阱:setTimeout 的“背叛”
来看一段经典代码:
const a = {
name: "Cherry",
func2() {
setTimeout(function() {
console.log(this.name); // ❌ undefined!this 指向 window
}, 1000);
}
};
a.func2();
为什么?因为 setTimeout 里的回调函数是裸调用的!this 跟 a 没半毛钱关系。
解法三连击:
✅ 方法一:that = this(传统艺能)
func2() {
const that = this;
setTimeout(function() {
console.log(that.name); // "Cherry"
}, 1000);
}
👴 老派但可靠,像你爸的旧皮带——土,但管用。
✅ 方法二:bind 强制绑定
setTimeout(function() {
console.log(this.name);
}.bind(this), 1000);
🔒 绑得死死的,this 别想跑。
✅ 方法三:箭头函数(现代解法)
func2() {
setTimeout(() => {
console.log(this.name); // "Cherry"!
}, 1000);
}
🎯 箭头函数没有自己的 this,它会继承外层作用域的 this。
所以在func2里,this 是a,箭头函数直接“蹭”过来用!
三、箭头函数:this 的“佛系青年”
“我不争不抢,没有 this,全靠继承。”
箭头函数:
- 没有
this - 没有
arguments - 不能当构造函数(
new会报错) - 没有
prototype
const arrow = () => {
console.log(this); // 外层作用域的 this(通常是 window 或模块上下文)
};
arrow(); // 输出 window(非严格模式)
⚠️ 所以别在对象方法里用箭头函数!否则 this 会“飘”走:
const bad = {
name: "Oops",
getName: () => {
console.log(this.name); // ❌ undefined!this 不是 bad
}
};
bad.getName(); // this 指向全局,name 不存在
四、面试高频题:你能答对几个?
Q1:以下代码输出什么?
var name = "windowName";
const obj = {
name: "Cherry",
getName: function() {
return this.name;
}
};
const fn = obj.getName;
console.log(fn());
✅ 答案:"windowName"(非严格模式)或 undefined(严格模式)
👉 因为 fn() 是裸调用!
Q2:如何让下面的代码输出 "Cherry"?
setTimeout(obj.getName, 1000);
✅ 答案(任选其一):
setTimeout(obj.getName.bind(obj), 1000)setTimeout(() => obj.getName(), 1000)setTimeout(function() { obj.getName(); }, 1000)(注意这里没用 this)
五、总结:this 的“驯服指南”
| 场景 | this 指向 | 应对策略 |
|---|---|---|
| 普通函数调用 | 全局 / undefined | 避免裸调用 |
| 对象方法调用 | 该对象 | ✅ 安全 |
| 回调函数(如 setTimeout) | 全局 | 用箭头函数 / bind / that |
| 箭头函数 | 外层作用域 | 别在对象方法里乱用 |
| 构造函数 | 新实例 | ✅ 正常使用 |
结语:this 不渣,只是你没读懂它
this 并不是 JavaScript 的 bug,而是一种动态上下文机制。它灵活,但也危险。理解它的规则,就像学会和一个性格多变的朋友相处——你知道他什么时候会“翻脸”,也知道怎么让他“听话”。
下次面试官问:“说说 this 的指向?”
你可以微微一笑:“this 啊,它从来不说‘我是谁’,只看‘谁叫我’。”
本文已通过“人类可读性测试”✅,保证不催眠,只催更。