在 JavaScript 的盛大舞会中,有两个神秘角色:
this—— 善变的舞伴,谁牵它手就跟谁跳;闭包(Closure)—— 那个站在角落、默默记住“曾经共舞之人”的怀旧者。
它们看似无关,却常在同一支舞曲中交织。今天,我们就把这对“舞会双子星”一起讲透!
🎭 一、this:只认当下,不念过往
🕺 普通函数:没人邀?和主人跳!
function dance() {
console.log(this.name); // 谁在牵我?
}
dance(); // 非严格 → window;严格 → undefined
“我不记得你是谁,我只看此刻谁拉我手。”
👯 对象方法:Alice 邀你,就只看 Alice
const alice = { name: 'Alice', dance() { console.log(this.name); } };
alice.dance(); // → "Alice"
但若把 dance 单独拿出来:
let f = alice.dance;
f(); // ❌ this 变了!因为调用者不再是 alice
“上一秒我是 Alice 的舞伴,这一秒……你是谁?”
💪 强行换舞伴:call / apply / bind
dance.call({ name: 'Charlie' }); // → "Charlie"
“规则由我定——这一曲,你必须和 Charlie 跳!”
🧊 箭头函数:痴情的守望者
const bob = {
name: 'Bob',
dance: () => console.log(this.name) // this 来自外层定义环境
};
bob.dance(); // ❌ 不是 Bob!通常是 window 或 undefined
“我的心上人,早在定义时就定下了。你们谁来邀我,我都不会动心。”
💌 二、闭包:记住过去,却不影响 this
闭包不是变量,而是一种能力——内层函数即使在外层函数执行完毕后,仍能访问其变量。
但请注意:闭包保留的是变量,不是 this!
🌰 经典陷阱:闭包 + 普通函数 = this 跑偏
window.name = "Global Han";
const dancer = {
name: "Dancer Han",
perform: function() {
this.name = "On Stage Han"; // 修改当前 this.name
function encore() {
console.log(this.name); // ❗ this 是谁?
}
return encore; // 返回内层函数 → 形成闭包
}
};
dancer.perform()(); // 输出 "Global Han"(非严格模式)
🔍 发生了什么?
perform()被dancer调用 →this指向dancer;encore是普通函数,在全局被调用 →this指向window;- 虽然
encore能通过闭包访问perform的作用域(比如局部变量),但它无法继承this; - 所以
this.name实际是window.name。
💬 闭包记住了“舞台上的灯光、音乐、道具”,
但没记住“谁是主角”——因为this不属于作用域链!
🛠️ 三、如何让闭包中的函数正确使用 this?
✅ 方法一:用 that 锁定当时的 this
perform: function() {
const that = this; // 把“此刻的舞伴”存下来
function encore() {
console.log(that.name); // ✅ 正确输出 "On Stage Han"
}
return encore;
}
闭包记住了
that,也就间接记住了“当时的主角”。
✅ 方法二:用箭头函数(推荐)
perform: function() {
const encore = () => {
console.log(this.name); // ✅ 箭头函数继承外层 this
};
return encore;
}
箭头函数没有自己的
this,直接沿用perform的this,
闭包 + 箭头 = 完美保留上下文!
🌟 四、终极比喻:舞会中的三人关系
| 角色 | 行为 | 特点 |
|---|---|---|
this | 谁牵我手,我就跟谁跳 | 动态绑定,只看调用方式 |
| 闭包 | 记住上一支舞的布景、台词、道具 | 静态捕获外层变量 |
| 箭头函数 | 从出生起就认定一个舞伴,永不更换 | this 在定义时锁定 |
💡 闭包能记住“那晚的月光”,但不会自动记住“那晚的舞伴”。
想让内层函数知道“主角是谁”?你得主动告诉它(用that)或选个痴情的箭头函数。
❤️🔥 总结金句
this不忠于感情,只忠于调用方式;- 闭包忠于记忆,但不负责传递
this;- 箭头函数是唯一能把“定义时的舞伴”刻进灵魂的函数;
- 想在闭包里用对
this?要么保存它,要么用箭头。
下次当你在代码中看到嵌套函数、回调、定时器,
别再问:“为什么 this 不是我想要的对象?”
而是问:“此刻,谁在牵它的手?我又有没有帮它记住真正的舞伴? ”
💃 在 JavaScript 的舞池里,
懂 this,懂闭包,你就是最优雅的舞者。