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
相关产品推荐
相关产品推荐

