this 不是你的心上人,它是 JavaScript 舞会上最现实的舞伴!

51 阅读3分钟

在 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"(非严格模式)

🔍 发生了什么?

  1. perform()dancer 调用 → this 指向 dancer
  2. encore 是普通函数,在全局被调用 → this 指向 window
  3. 虽然 encore 能通过闭包访问 perform 的作用域(比如局部变量),但它无法继承 this
  4. 所以 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,直接沿用 performthis
闭包 + 箭头 = 完美保留上下文!


🌟 四、终极比喻:舞会中的三人关系

角色行为特点
this谁牵我手,我就跟谁跳动态绑定,只看调用方式
闭包记住上一支舞的布景、台词、道具静态捕获外层变量
箭头函数从出生起就认定一个舞伴,永不更换this 在定义时锁定

💡 闭包能记住“那晚的月光”,但不会自动记住“那晚的舞伴”。
想让内层函数知道“主角是谁”?你得主动告诉它(用 that)或选个痴情的箭头函数。


❤️‍🔥 总结金句

  • this 不忠于感情,只忠于调用方式
  • 闭包忠于记忆,但不负责传递 this
  • 箭头函数是唯一能把“定义时的舞伴”刻进灵魂的函数
  • 想在闭包里用对 this?要么保存它,要么用箭头

下次当你在代码中看到嵌套函数、回调、定时器,
别再问:“为什么 this 不是我想要的对象?”
而是问:“此刻,谁在牵它的手?我又有没有帮它记住真正的舞伴?

💃 在 JavaScript 的舞池里,
this,懂闭包,你就是最优雅的舞者。