You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JS两种闭包实现的变量更新效果差异原因是什么

核心差异本质:两个案例的score变量所属的作用域生命周期完全不同,且第一个案例并未形成闭包

你之前对闭包的认知有一个小偏差:不是只要把函数作为事件回调传入就一定会生成闭包,闭包生成的核心前置条件是:函数访问了自身作用域以外的外层作用域变量,且该函数在其原始定义的作用域销毁后仍被执行。

第一个案例的执行逻辑

function increaseScore(){
   var score = 0;
   score ++;
   console.log(score);
}
document.addEventListener("click", increaseScore);

这个案例里的increaseScore是全局作用域下定义的函数,没有访问任何外层作用域的变量,完全不满足闭包的生成条件:

  • 每次点击事件触发调用increaseScore时,都会创建全新的函数执行上下文,var score = 0是每次调用时都会重新声明、赋值的局部变量
  • 函数执行完毕后,当前执行上下文销毁,score变量也随之被回收,下一次调用又会生成全新的score,自然每次输出都是1。

第二个案例的执行逻辑

function addScore(){
  var score = 0;
  return function (){
   score ++;
   console.log(score);
  }
}
var keepScore = addScore();
document.addEventListener("click", keepScore);

这个案例里的匿名函数满足闭包的生成条件:

  • 匿名函数定义在addScore的作用域内,并且访问了外层addScore作用域里的score变量
  • 调用addScore()时,会先创建addScore的执行上下文,声明score = 0,随后返回匿名函数赋值给keepScore。原本addScore执行完上下文就应该销毁,但因为匿名函数捕获了score变量的引用,形成闭包,score所在的作用域会被保留在内存中不会被回收
  • 每次点击调用keepScore时,修改的都是同一个被闭包保留的score变量,所以数值会持续累加。

补充:事件回调生成闭包的正确示例

如果你想通过事件回调的方式生成闭包,只需要让回调函数访问外层作用域的变量即可,比如你提到的IIFE场景:

// IIFE执行完无返回值,但回调形成闭包,score会持续累加
(function(){
  var score = 0;
  function increaseScore(){
    score ++;
    console.log(score);
  }
  document.addEventListener("click", increaseScore);
})()

这个案例里的increaseScore捕获了IIFE作用域内的score变量,被作为事件回调传到IIFE作用域外执行,因此形成闭包,score会被持久保留,每次点击数值都会累加。

内容的提问来源于stack exchange,提问作者Kevo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 00:54:02