最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript 閉包:函數(shù)背后的“背包”(一文詳解)

 更新時(shí)間:2026年05月25日 09:17:58   作者:yqcoder  
文章詳細(xì)解釋了JavaScript閉包的概念及其作用,包括數(shù)據(jù)封裝、函數(shù)柯里化和異步回調(diào)中的的狀態(tài)保持,并通過(guò)實(shí)例展示了閉閉如何工作以及如何避免潛在的內(nèi)存泄漏問(wèn)題,感興趣的朋友一起看看吧

?? 什么是閉包?

官方定義
閉包是指有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中變量的函數(shù)。

通俗解釋
想象一個(gè)函數(shù)是一個(gè),他出生時(shí)背了一個(gè)背包(Scope/作用域)。
這個(gè)背包里裝著他出生時(shí)周?chē)h(huán)境中的所有變量。
即使這個(gè)人走到了世界的盡頭(全局環(huán)境),或者他的父母(外部函數(shù))已經(jīng)去世(執(zhí)行完畢),他依然背著那個(gè)背包,可以隨時(shí)拿出里面的東西使用。

一句話總結(jié)
閉包 = 函數(shù) + 函數(shù)能訪問(wèn)到的自由變量(背包里的東西)。

1. ?? 核心原理:為什么會(huì)有閉包?

要理解閉包,首先要理解 JS 的詞法作用域(Lexical Scoping)垃圾回收機(jī)制

正常情況:函數(shù)執(zhí)行完,變量銷(xiāo)毀

function outer() {
  let a = 10;
  console.log(a);
}
outer(); // 10
// 函數(shù)執(zhí)行完畢,a 被垃圾回收器回收,內(nèi)存釋放。

特殊情況:內(nèi)部函數(shù)引用了外部變量

function outer() {
  let a = 10;
  function inner() {
    console.log(a); // inner 引用了 outer 的變量 a
  }
  return inner; // 將 inner 返回出去
}
const myFunc = outer();
myFunc(); // 10

發(fā)生了什么?

  1. outer 執(zhí)行完畢,按理說(shuō) a 應(yīng)該被銷(xiāo)毀。
  2. 但是,inner 函數(shù)被返回并賦值給了 myFunc。
  3. inner 的定義中包含了對(duì) a 的引用。
  4. JS 引擎發(fā)現(xiàn):“嘿,inner 還在外面被人拿著呢,它還要用 a,所以我不能銷(xiāo)毀 a!”
  5. 于是,a 所在的內(nèi)存空間被保留下來(lái),形成了閉包。

2. ??? 閉包的三大核心作用

閉包不僅僅是理論,它在實(shí)際開(kāi)發(fā)中有三個(gè)非常強(qiáng)大的用途:

? 作用一:數(shù)據(jù)封裝與私有變量(模擬私有屬性)

在 ES6 Class 出現(xiàn)之前,JS 沒(méi)有真正的“私有變量”。閉包是實(shí)現(xiàn)模塊化和數(shù)據(jù)隱藏的核心手段。

場(chǎng)景:創(chuàng)建一個(gè)計(jì)數(shù)器,外部只能調(diào)用 increment,無(wú)法直接修改 count

function createCounter() {
  let count = 0; // 私有變量,外部無(wú)法直接訪問(wèn)
  return {
    increment: function () {
      count++;
      return count;
    },
    decrement: function () {
      count--;
      return count;
    },
    getCount: function () {
      return count;
    },
  };
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.count); // undefined ? 無(wú)法直接訪問(wèn)

價(jià)值:保護(hù)數(shù)據(jù)不被意外篡改,提供清晰的 API 接口。

? 作用二:函數(shù)柯里化與參數(shù)復(fù)用

閉包可以“記住”之前傳入的參數(shù),從而實(shí)現(xiàn)函數(shù)的部分應(yīng)用(Partial Application)。

場(chǎng)景:固定稅率計(jì)算。

function makeTaxCalculator(taxRate) {
  // taxRate 被閉包“記住”了
  return function (price) {
    return price * taxRate;
  };
}
const calcVAT = makeTaxCalculator(0.13); // 增值稅率 13%
const calcIncomeTax = makeTaxCalculator(0.2); // 所得稅率 20%
console.log(calcVAT(100)); // 13
console.log(calcIncomeTax(100)); // 20

價(jià)值:減少重復(fù)傳參,提高代碼復(fù)用性。

? 作用三:異步回調(diào)中的狀態(tài)保持

在異步編程(如定時(shí)器、AJAX、事件監(jiān)聽(tīng))中,閉包確?;卣{(diào)函數(shù)執(zhí)行時(shí),依然能訪問(wèn)到定義時(shí)的上下文變量。

場(chǎng)景:延遲打印日志。

function logAfterDelay(msg, delay) {
  setTimeout(function () {
    // 這里的 msg 和 delay 即使在 setTimeout 觸發(fā)時(shí)(幾秒后)依然可用
    console.log(`${msg} after ${delay}ms`);
  }, delay);
}
logAfterDelay("Hello", 1000);

3. ?? 代碼實(shí)戰(zhàn):經(jīng)典面試題解析

? 經(jīng)典陷阱:循環(huán)中的閉包

這是面試中最??嫉拈]包問(wèn)題。

for (var i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i);
  }, i * 1000);
}

預(yù)期輸出:1, 2, 3, 4, 5
實(shí)際輸出:6, 6, 6, 6, 6 ??

原因

  • var 沒(méi)有塊級(jí)作用域,所有定時(shí)器共享同一個(gè)全局變量 i
  • 當(dāng)定時(shí)器執(zhí)行時(shí),循環(huán)已經(jīng)結(jié)束,i變成了 6。

? 解決方案 1:使用 IIFE(立即執(zhí)行函數(shù))創(chuàng)建閉包

for (var i = 1; i <= 5; i++) {
  (function (j) {
    setTimeout(function timer() {
      console.log(j); // j 是每次循環(huán)獨(dú)立的副本
    }, j * 1000);
  })(i);
}

原理:IIFE 為每次循環(huán)創(chuàng)建了一個(gè)新的作用域,將當(dāng)前的 i 值作為參數(shù) j 傳入并保存。

? 解決方案 2:使用let(推薦)

for (let i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i);
  }, i * 1000);
}

原理let 具有塊級(jí)作用域,JS 引擎會(huì)為每次循環(huán)迭代創(chuàng)建一個(gè)新的綁定。

4. ?? 雙刃劍:內(nèi)存泄漏與性能

閉包雖然強(qiáng)大,但濫用會(huì)導(dǎo)致內(nèi)存泄漏。

?? 為什么會(huì)導(dǎo)致內(nèi)存泄漏?

正常情況下,函數(shù)執(zhí)行完后,局部變量會(huì)被回收。
但如果形成了閉包,且外部一直持有對(duì)該閉包函數(shù)的引用,那么閉包所引用的所有外部變量都不會(huì)被回收

危險(xiǎn)示例

function hugeDataHandler() {
  const hugeArray = new Array(1000000).fill("data"); // 占用大量?jī)?nèi)存
  return function () {
    console.log(hugeArray.length); // 只要這個(gè)函數(shù)存在,hugeArray 就不會(huì)被回收
  };
}
const handler = hugeDataHandler();
// 如果你不再需要 handler,必須手動(dòng)解除引用
handler = null; // ? 允許 GC 回收 hugeArray

??? 最佳實(shí)踐

  1. 及時(shí)解除引用:如果不再需要閉包函數(shù),將其賦值為 null。
  2. 避免在閉包中引用巨大的無(wú)用對(duì)象
  3. 謹(jǐn)慎使用全局閉包:盡量縮小閉包的作用范圍。

?? 總結(jié)

特性說(shuō)明
本質(zhì)函數(shù) + 引用的自由變量
核心能力延長(zhǎng)變量生命周期,實(shí)現(xiàn)數(shù)據(jù)私有化
主要用途1. 封裝私有變量
2. 柯里化/參數(shù)復(fù)用
3. 異步回調(diào)狀態(tài)保持
副作用可能導(dǎo)致內(nèi)存泄漏,增加內(nèi)存占用
解決循環(huán)陷阱使用 let 或 IIFE

到此這篇關(guān)于JavaScript 閉包:函數(shù)背后的“背包”的文章就介紹到這了,更多相關(guān)js閉包內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解JS鼠標(biāo)事件中clientX/screenX/offsetX/pageX屬性的區(qū)別

    詳解JS鼠標(biāo)事件中clientX/screenX/offsetX/pageX屬性的區(qū)別

    在熟悉業(yè)務(wù)中播放器功能的時(shí)候,發(fā)現(xiàn)時(shí)間軸上綁定了點(diǎn)擊和拖動(dòng)事件,頻繁遇到了類(lèi)似pageX之類(lèi)的事件屬性,所以本文就來(lái)和大家詳細(xì)聊聊clientX/screenX/offsetX/pageX等屬性的區(qū)別吧
    2024-03-03
  • pnpm?monorepo?聯(lián)調(diào)方案問(wèn)題解析

    pnpm?monorepo?聯(lián)調(diào)方案問(wèn)題解析

    文章主要介紹了在pnpmmonorepo環(huán)境下進(jìn)行多庫(kù)聯(lián)調(diào)的方案,包括使用`pnpmlink`命令來(lái)鏈接指定的文件夾或全局的`node_modules`,并在項(xiàng)目中通過(guò)`pnpmlink--global<pkg>`來(lái)引用這些庫(kù),本文給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2024-12-12
  • JS實(shí)現(xiàn)點(diǎn)擊按鈕獲取頁(yè)面高度的方法

    JS實(shí)現(xiàn)點(diǎn)擊按鈕獲取頁(yè)面高度的方法

    這篇文章主要介紹了JS實(shí)現(xiàn)點(diǎn)擊按鈕獲取頁(yè)面高度的方法,涉及JavaScript針對(duì)頁(yè)面元素高度的各種常見(jiàn)運(yùn)算,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-11-11
  • 在ES5與ES6環(huán)境下處理函數(shù)默認(rèn)參數(shù)的實(shí)現(xiàn)方法

    在ES5與ES6環(huán)境下處理函數(shù)默認(rèn)參數(shù)的實(shí)現(xiàn)方法

    本文給大家介紹在ES5與ES6環(huán)境下處理函數(shù)默認(rèn)參數(shù)的實(shí)現(xiàn)方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,感興趣的朋友跟隨腳本之家小編一起學(xué)習(xí)吧
    2018-05-05
  • 兼容主流瀏覽器的iframe自適應(yīng)高度js腳本

    兼容主流瀏覽器的iframe自適應(yīng)高度js腳本

    兼容主流瀏覽器的 iframe 自適應(yīng)高度,很實(shí)用的,具體的js腳本如下,感興趣的朋友可不要錯(cuò)過(guò)
    2014-01-01
  • JavaScript中的私有成員

    JavaScript中的私有成員

    JavaScript中的私有成員...
    2006-09-09
  • 基于JS快速實(shí)現(xiàn)導(dǎo)航下拉菜單動(dòng)畫(huà)效果附源碼下載

    基于JS快速實(shí)現(xiàn)導(dǎo)航下拉菜單動(dòng)畫(huà)效果附源碼下載

    這是一個(gè)帶變形動(dòng)畫(huà)特效的下拉導(dǎo)航菜單特效。該導(dǎo)航菜單在菜單項(xiàng)之間切換時(shí),下拉菜單會(huì)快速的根據(jù)菜單內(nèi)容的大小來(lái)動(dòng)態(tài)變形,顯示合適的下拉菜單大小,效果非常棒,對(duì)導(dǎo)航下拉菜單效果感興趣的朋友一起通過(guò)本文學(xué)習(xí)吧
    2016-10-10
  • JavaScript實(shí)現(xiàn)左側(cè)菜單效果

    JavaScript實(shí)現(xiàn)左側(cè)菜單效果

    這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)左側(cè)菜單效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • Array數(shù)組對(duì)象中的forEach、map、filter及reduce詳析

    Array數(shù)組對(duì)象中的forEach、map、filter及reduce詳析

    這篇文章主要給大家介紹了關(guān)于Array數(shù)組對(duì)象中forEach、map、filter及reduce的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用array數(shù)據(jù)具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-08-08
  • Vue編程式跳轉(zhuǎn)的實(shí)例代碼詳解

    Vue編程式跳轉(zhuǎn)的實(shí)例代碼詳解

    這篇文章主要介紹了Vue編程式跳轉(zhuǎn)的實(shí)例代碼,文中給大家介紹了vue 編程式j(luò)s跳轉(zhuǎn)路由,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-07-07

最新評(píng)論

西平县| 罗定市| 上林县| 嘉荫县| 泰和县| 阳江市| 城口县| 华坪县| 随州市| 桃源县| 镇雄县| 玛多县| 黔南| 报价| 台北市| 娄烦县| 惠东县| 吉林市| 特克斯县| 稷山县| 靖边县| 双峰县| 濮阳县| 韶关市| 霍城县| 益阳市| 墨竹工卡县| 陇川县| 涞源县| 克山县| 青田县| 寻甸| 科技| 牡丹江市| 遵义县| 武强县| 民丰县| 诏安县| 镇巴县| 济阳县| 枞阳县|