凉凉凉了 發表於 2026-1-27 10:42:00

JS 里的 “变量租房记”:闭包是咋把变量 “扣” 下来的?

<h1 data-id="heading-0">🧑‍💻 写在开头</h1>
<p>点赞 + 收藏 === 学会🤣🤣🤣</p>
<div>
<div>
<h2 data-id="heading-0">前言</h2>
<p>你有没有过这种疑惑:</p>
<p>明明在 “出租屋”(函数)里放的 “行李”(变量),房东都退房了,这行李咋还能拿出来用?或者循环里的变量总 “串房间”,明明住 1 号房,结果跑到 6 号房去了?在函数里定义的变量,出了函数居然还能用?</p>
<p>这背后其实藏着<strong>作用域、作用域链</strong>的 “小九九”,而最终的 <strong>“幕后BOSS”</strong> 就是 ——<strong>闭包</strong>。今天咱们就用 “租房” ,把<strong>作用域、作用域链、闭包</strong>这仨 JS 里的 “租房规则” 讲明白,让你迅速成为<strong>闭包大师</strong>。</p>
<h3 data-id="heading-1">一、先搞懂:变量的 “租房地盘” 叫作用域</h3>
<p>变量不是随便 “住” 的,它得有自己的 “房子”—— 这就是<strong>作用域</strong>。简单说:<strong>变量能待的地方,就是它的作用域</strong>。</p>
<p>JS 里的 “房子” 分 3 种:</p>
<h4 data-id="heading-2">1. 全局作用域:“大街上的共享行李,谁都能碰”</h4>
<p>你把行李放 “大街上”(函数 / 大括号外面),那整个小区(整个文件)的人都能拿。</p>
<p>比如:</p>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">var good = "张三的快递"; // 放大街上(全局作用域)
function getGood() {
    console.log(good);   // 谁都能拿,输出“张三的快递”
}
getGood();</pre>
</div>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202601/2149129-20260127103848358-1882709269.png" alt="ScreenShot_2026-01-27_103211_061" loading="lazy"></p>
<h4 data-id="heading-3">2. 函数作用域:“自家卧室的行李,外人别进”</h4>
<p>你把行李锁在 “卧室”(函数内部)里,那只有卧室里的人能拿,出了门就碰不到了。</p>
<p>比如:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">var wallet = "全局钱包(100块)";
function myHome() {
    var wallet = "卧室钱包(2000块)"; // 卧室里的钱包,只在卧室有效
    console.log(wallet); // 用的是卧室里的,输出2000
}
myHome();
console.log(wallet); // 只能拿到大街上的,输出100</pre>
</div>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202601/2149129-20260127103921380-1263797640.png" alt="ScreenShot_2026-01-27_103224_386" loading="lazy"></p>
<div>
<div>
<p><strong>划重点</strong>:这房子是 “单向门”—— 只能从卧室(内部)往外拿大街上的东西,大街上的人拿不到卧室里的。</p>
<h4 data-id="heading-4">3. 块级作用域:“用<code>let</code>给行李划个小格子”</h4>
<p><code>var</code>是 “粗心租客”:哪怕把行李放 “衣柜”(<code>if/for</code>的<code>{}</code>里),也会不小心 “掉” 到卧室里;但<code>let/const</code>是 “细心租客”,把行李锁在衣柜格子里,只有格子里能拿。</p>
<p>上代码:</p>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">if (true) {
    let money = 500; // 锁在衣柜格子(块级作用域)里
}
console.log(money); // 报错!外面打不开这格子</pre>
</div>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202601/2149129-20260127103946356-953340697.png" alt="ScreenShot_2026-01-27_103243_401" loading="lazy"></p>
<div>
<div>
<p>结果也是显示了<strong>money is not defined</strong>。所以当形成块级作用域时,外层是访问不了内层的。要是换成<code>var money = 500;</code>,这钱就会 “掉” 到外面,能被拿到 —— 这就是<code>let</code>的 “格子锁” 功能。</p>
<h3 data-id="heading-5">二、变量 “找不到?喊房东!”—— 作用域链</h3>
<p>你在卧室里找行李,找不到咋办?喊 “房东”(外层作用域)啊!房东找不到,就喊 “大房东”(外外层作用域),一层一层往上喊,这串 “喊人的链条” 就是<strong>作用域链</strong>。</p>
<p>看个例子:</p>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">function myHome() {
    var TV = "客厅的大电视";
    let sofa = "客厅沙发";
    {
      let sofa = "卧室小沙发";
      var eat = "客厅零食"; // var没格子锁,掉客厅里了
      let clothes = "卧室睡衣";
      console.log(TV); // 卧室里找不到,喊房东(客厅)→ 找到电视,输出“大电视”
      console.log(sofa); // 卧室里有小沙发,直接拿→ 输出“小沙发”
    }
    console.log(sofa); // 客厅里的大沙发→ 输出“客厅沙发”
    console.log(eat); // 零食掉客厅了→ 输出“客厅零食”
    console.log(clothes); // 睡衣锁在卧室格子里→ 报错!拿不到
}
myHome();</pre>
</div>
<p>结果跟我们想的一样:</p>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202601/2149129-20260127104021027-1217874570.png" alt="ScreenShot_2026-01-27_103256_908" loading="lazy"></p>
<p>&nbsp;大致分析如下:</p>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202601/2149129-20260127104037283-149183493.png" alt="ScreenShot_2026-01-27_103307_181" loading="lazy"></p>
<div>
<div>
<p><strong>关键规律</strong>:你租房子的时候(函数定义时),就会记下来 “房东是谁”(用<code>outer</code>指针)—— 不管你后来搬到哪(函数在哪调用),找东西都得按 “最初的房东” 来!</p>
<h3 data-id="heading-6">三、闭包:“租客退租了,行李却被保洁扣下了”</h3>
<p>先记两个租房规矩:</p>
<ol>
<li>租客退租(函数执行完),卧室里的东西(执行上下文)会被物业清走(内存销毁);</li>
<li>保洁(内部函数)能进租客的卧室拿东西(作用域规则)。</li>
</ol>
<p>那闭包是咋回事?</p>
<p>别急,先看个例子:</p>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">function leave() {
var left = "租客的笔记本电脑"; // 卧室里的行李
function cleaner() { // 保洁是卧室里的“内部员工”
    console.log(left); // 保洁能拿这行李
}
return cleaner; // 租客把保洁“带走了”
}
var cleaner1 = leave(); // 租客退租了,卧室该被清了
cleaner1(); // 但保洁居然拿出了“笔记本电脑”!</pre>
</div>
最喜欢的输出结果环节:<br>
</div>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202601/2149129-20260127104106270-1164742115.png" alt="ScreenShot_2026-01-27_103318_696" loading="lazy"></p>
<div>
<div>
<p>这就离谱了:租客都走了,行李咋还在?</p>
<p><strong>答案</strong>:因为保洁(内部函数)还 “惦记着” 这行李,物业(JS 引擎)就不会把卧室全清掉 —— 而是留个 “小储物箱”(这就是闭包),专门装保洁需要的行李(这里就是<code>遗留行李</code>)。</p>
<p>大白话翻译闭包:</p>
<blockquote>
<p>租客退租了,但保洁把他的行李扣在储物箱里,走到哪带到哪 —— 这储物箱就是闭包。</p>
</blockquote>
<p>还没懂的话我再举个例子:</p>
<p>假设你有一个不听话的儿子,他跟你断绝关系出去闯荡,幸运的是他出去之后你们的老房子就拆迁了,于是你拿着拆迁补贴买了一套新房子,但你于心不忍,你怕儿子走投无路回来找你,但是房子又拆迁了,这时候你就会在这个拆迁遗址<strong>立一个牌子</strong>或者什么做<strong>标记</strong>,让你儿子知道你搬到哪里了,而这个<strong>牌子</strong>就是<strong>闭包</strong>。虽然老房子没了,但是这个<strong>牌子</strong>有它的作用----提醒你儿子新家的地址,你儿子如果回来会用得到这个作用。</p>
<p>虽然已经出栈,但依旧保留函数要用到的方法,这就是<strong>闭包</strong>的核心。</p>
<p>再看一个例子:为啥能输出 1-5?就是因为闭包把 “每个房间的行李” 都扣下来了:</p>
</div>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">var arr = [];
for (var i = 1; i &lt;= 5; i++) {
    function foo(j) {
      arr.push(function(){
            console.log(j);
      })
    }
    foo(i);
}
for (let n = 0; n &lt; arr.length; n++) {
    arr();
}</pre>
</div>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202601/2149129-20260127104127943-1792403126.png" alt="ScreenShot_2026-01-27_103328_397" loading="lazy"></p>
<div>
<div>
<h3 data-id="heading-7">四、闭包:是 “储物神器” 也是 “占房坑”</h3>
<h4 data-id="heading-8">优点:</h4>
<ul>
<li><strong>藏私房钱</strong>:想把行李锁起来,只有自己能拿?用闭包!</li>
<li><strong>封装 “专属房间”</strong> :早期 JS 没 “独立公寓”(模块),全靠闭包封出专属空间。</li>
</ul>
<h4 data-id="heading-9">缺点:</h4>
<ul>
<li><strong>占着房间不撒手(内存泄露)</strong> :闭包扣着行李,物业就没法清房间 —— 要是不用的闭包不扔,房间会越占越多。解决办法:不用保洁的时候,把保洁辞了(比如<code>离职保洁 = null</code>),物业就会把储物箱清掉。</li>
</ul>
<h2 data-id="heading-10">总结:一句话串起所有 “租房规则”</h2>
<p><strong>作用域</strong>是变量的 “房间”,<strong>作用域链</strong>是 “找不到东西喊房东的顺序”,<strong>闭包</strong>是 “租客退租后,保洁扣下行李的储物箱”—— 这仨就是 JS 里 “变量住哪、咋找、咋留” 的全部逻辑!</p>
<blockquote>
<p>恭喜你成为<strong>闭包大师</strong>!</p>
</blockquote>
</div>
<div>
<h3 id="tid-D8HBxE">如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。</h3>
</div>
<p><em><img src="https://img2024.cnblogs.com/blog/2149129/202501/2149129-20250122165814748-630765389.png" alt="" loading="lazy"></em></p>
</div>
</div>
</div>
</div><br><br>
来源:https://www.cnblogs.com/smileZAZ/p/19537076
頁: [1]
查看完整版本: JS 里的 “变量租房记”:闭包是咋把变量 “扣” 下来的?