欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript中閉包(Closure)舉例深度講解

 更新時間:2025年09月29日 10:40:04   作者:超級無敵大蟑王  
閉包可以用來模擬塊級作用域,從而避免變量污染和命名沖突,下面這篇文章主要介紹了JavaScript中閉包(Closure)的相關資料,文中通過代碼介紹非常詳細,需要的朋友可以參考下

1.什么是閉包:

閉包是JavaScript中最強大且獨特的特性之一,它是函數(shù)與其詞法環(huán)境的組合。閉包使得函數(shù)能夠訪問其外部作用域的變量,即使外部函數(shù)已經(jīng)執(zhí)行完畢。這種機制讓JavaScript具備了許多其他語言需要復雜語法才能實現(xiàn)的功能。

// 最簡單的閉包示例
function outer() {
  const message = "Hello from outer!"; // 外部變量
  function inner() {
    console.log(message); // inner函數(shù)"記住"了message
  }
  
  return inner; // 返回inner函數(shù)
}

const myClosure = outer(); 
myClosure(); // "Hello from outer!"

2. 核心概念與前置知識:

2.1 執(zhí)行上下文 (Execution Context):

執(zhí)行上下文是 JavaScript 代碼執(zhí)行時的環(huán)境,每當代碼執(zhí)行時都會創(chuàng)建對應的執(zhí)行上下文。

執(zhí)行上下文的組成部分:

// 偽代碼:執(zhí)行上下文的內(nèi)部結構
ExecutionContext = {
  // 1. 詞法環(huán)境 (用于let/const和函數(shù)聲明)
  LexicalEnvironment: {
    EnvironmentRecord: {}, // 環(huán)境記錄
    outer: null            // 外部環(huán)境引用
  },
  
  // 2. 變量環(huán)境 (用于var聲明)
  VariableEnvironment: {
    EnvironmentRecord: {},
    outer: null
  },
  
  // 3. this綁定
  ThisBinding: undefined
}

2.2 詞法環(huán)境 (Lexical Environment):

詞法環(huán)境是存儲標識符-變量映射的結構,由環(huán)境記錄和外部環(huán)境引用組成。

詞法環(huán)境的創(chuàng)建過程:

function createLexicalEnvironment() {
  // 示例:詞法環(huán)境的創(chuàng)建
  function outer(x) {
    let a = 10;
    const b = 20;
    
    function inner(y) {
      let c = 30;
      console.log(a + b + c + x + y); // 訪問多個作用域的變量
    }
    
    return inner;
  }
  
  return outer;
}

// 執(zhí)行過程中的詞法環(huán)境變化
/*
1. 全局詞法環(huán)境:
{
  EnvironmentRecord: {
    createLexicalEnvironment: <function>,
    outer: <function>
  },
  outer: null
}

2. outer函數(shù)的詞法環(huán)境:
{
  EnvironmentRecord: {
    x: 參數(shù)值,
    a: 10,
    b: 20,
    inner: <function>
  },
  outer: <全局詞法環(huán)境的引用>
}

3. inner函數(shù)的詞法環(huán)境:
{
  EnvironmentRecord: {
    y: 參數(shù)值,
    c: 30
  },
  outer: <outer函數(shù)詞法環(huán)境的引用>
}
*/

2.3 作用域鏈 (Scope Chain)

作用域鏈是通過詞法環(huán)境的外部引用形成的鏈條,用于標識符解析。

作用域鏈查找算法

// 作用域鏈查找示例
function demonstrateScopeChain() {
  const globalVar = "全局變量";
  function level1() {
    const level1Var = "第一層變量";
    function level2() {
      const level2Var = "第二層變量";
      function level3() {
        const level3Var = "第三層變量";
        // 變量查找順序演示
        console.log(level3Var); // 1. 在當前環(huán)境找到
        console.log(level2Var); // 2. 向上一層查找
        console.log(level1Var); // 3. 繼續(xù)向上查找
        console.log(globalVar);  // 4. 查找到全局環(huán)境
        // console.log(nonExistent); // 5. 找不到則報錯
      }
      return level3;
    }
    return level2;
  }
  return level1;
}

// 調(diào)用過程中的作用域鏈
const fn = demonstrateScopeChain()()();
fn(); // 執(zhí)行時會沿著作用域鏈查找變量

3. 代碼示例與分析:

3.1 經(jīng)典的閉包示例

function makeCounter() {
  let count = 0; // 外部函數(shù)的局部變量
  
  return function() { // 返回的內(nèi)部函數(shù)形成閉包
    count++; // 訪問外部函數(shù)的變量
    return count;
  };
}

const counter = makeCounter(); // 調(diào)用外部函數(shù)
console.log(counter()); // 1 - 調(diào)用閉包函數(shù)
console.log(counter()); // 2 - count 變量被保持
console.log(counter()); // 3

3.2 逐行執(zhí)行過程分析

詳細步驟解析:

// 步驟 1-2: 全局執(zhí)行上下文創(chuàng)建,調(diào)用makeCounter
function makeCounter() {
  // 步驟 3: 在makeCounter的詞法環(huán)境中創(chuàng)建count變量
  let count = 0;
  
  // 步驟 4: 創(chuàng)建匿名函數(shù),該函數(shù)的[[Environment]]屬性
  // 指向makeCounter的詞法環(huán)境,形成閉包
  return function() {
    // 步驟 9: 通過作用域鏈找到外部的count變量
    count++;
    return count;
  };
  // 步驟 5-6: makeCounter執(zhí)行完畢,執(zhí)行上下文出棧
  // 但count變量因為被閉包引用而不會被垃圾回收
}

// 步驟 7: counter變量保存了閉包函數(shù)的引用
const counter = makeCounter();

// 步驟 8-10: 每次調(diào)用counter()都會訪問保存的count變量
console.log(counter()); // 1

3.3 V8 引擎的優(yōu)化

V8 引擎對閉包進行了以下優(yōu)化:

  1. 變量提升優(yōu)化:只保留被閉包實際使用的外部變量
  2. 內(nèi)存管理:未被引用的外部變量會被垃圾回收
  3. 作用域分析:在編譯時分析變量使用情況
function optimizationExample() {
  let used = "被閉包使用的變量";
  let unused = "未被使用的變量"; // V8會優(yōu)化掉這個變量
  let alsoUnused = "同樣未被使用";
  
  return function() {
    console.log(used); // 只有這個變量會被保留在閉包中
  };
}

注意:在調(diào)試時,由于V8的優(yōu)化,某些未使用的變量可能在調(diào)試器中顯示為 “undefined”。

4. 經(jīng)典應用場景與最佳實踐:

4.1 模塊化封裝

const Calculator = (function() {
  let result = 0; // 私有變量
  
  return {
    add: function(x) {
      result += x;
      return this;
    },
    multiply: function(x) {
      result *= x;
      return this;
    },
    getResult: function() {
      return result;
    },
    reset: function() {
      result = 0;
      return this;
    }
  };
})();

// 使用
Calculator.add(5).multiply(2).getResult(); // 10

4.2 事件處理與回調(diào)

function createButtonHandler(name) {
  return function(event) {
    console.log(`按鈕 ${name} 被點擊了`);
    // name變量被閉包保存
  };
}

document.getElementById('btn1').onclick = createButtonHandler('按鈕1');
document.getElementById('btn2').onclick = createButtonHandler('按鈕2');

4.3 防抖和節(jié)流

// 防抖函數(shù)
function debounce(func, delay) {
  let timeoutId; // 被閉包保存的變量
  
  return function(...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => {
      func.apply(this, args);
    }, delay);
  };
}

// 節(jié)流函數(shù)
function throttle(func, delay) {
  let lastCall = 0;
  
  return function(...args) {
    const now = Date.now();
    if (now - lastCall >= delay) {
      lastCall = now;
      func.apply(this, args);
    }
  };
}

5. 常見陷阱與解決方案:

5.1 循環(huán)中的閉包陷阱

問題代碼:

// 經(jīng)典錯誤示例
for (var i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i); // 輸出三次 3
  }, 100);
}

解決方案:

// 方案1:使用IIFE創(chuàng)建新的作用域
for (var i = 0; i < 3; i++) {
  (function(j) {
    setTimeout(function() {
      console.log(j); // 輸出 0, 1, 2
    }, 100);
  })(i);
}

// 方案2:使用let塊級作用域
for (let i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i); // 輸出 0, 1, 2
  }, 100);
}

// 方案3:使用bind
for (var i = 0; i < 3; i++) {
  setTimeout(function(j) {
    console.log(j); // 輸出 0, 1, 2
  }.bind(null, i), 100);
}

5.2 內(nèi)存泄漏風險

// 可能導致內(nèi)存泄漏的代碼
function createHandler() {
  const largeData = new Array(1000000).fill('data'); // 大量數(shù)據(jù)
  
  return function() {
    // 即使不使用largeData,它也會被閉包保留
    console.log('handler called');
  };
}

// 解決方案:顯式釋放不需要的引用
function createHandler() {
  const largeData = new Array(1000000).fill('data');
  const needed = largeData.slice(0, 10); // 只保留需要的部分
  
  return function() {
    console.log(needed.length);
    // largeData會被垃圾回收
  };
}

總結 

到此這篇關于JavaScript中閉包(Closure)的文章就介紹到這了,更多相關JS閉包Closure內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • JavaScript中Promise的執(zhí)行順序詳解

    JavaScript中Promise的執(zhí)行順序詳解

    Promise 是 JS 中進行異步編程的新的解決方案(舊的是純回調(diào)形式) ,下面這篇文章主要給大家介紹了關于JavaScript中Promise執(zhí)行順序的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-01-01
  • javascript Array對象基礎知識小結

    javascript Array對象基礎知識小結

    感覺自己對Array對象總是有種朦朧的感覺,今天自己手寫總結,加深一下印象。
    2010-11-11
  • 用js來格式化字符串示例模仿css

    用js來格式化字符串示例模仿css

    用js來格式化字符串示例模仿css...
    2007-04-04
  • JS無縫滾動效果實現(xiàn)方法分析

    JS無縫滾動效果實現(xiàn)方法分析

    這篇文章主要介紹了JS無縫滾動效果實現(xiàn)方法,結合實例形式較為詳細的分析了無縫滾動的原理、實現(xiàn)技巧與相關注意事項,需要的朋友可以參考下
    2016-12-12
  • 前端實現(xiàn)添加水印功能的四種方法

    前端實現(xiàn)添加水印功能的四種方法

    這篇文章主要介紹了前端實現(xiàn)添加水印功能的四種方法,分別是使用CSS、Canvas、SVG以及動態(tài)生成水印圖像,每種方法都有其特點和適用場景,并且每種方法都給出了實例代碼,需要的朋友可以參考下
    2025-03-03
  • JS實現(xiàn)模擬百度搜索“2012世界末日”網(wǎng)頁地震撕裂效果代碼

    JS實現(xiàn)模擬百度搜索“2012世界末日”網(wǎng)頁地震撕裂效果代碼

    這篇文章主要介紹了JS實現(xiàn)模擬百度搜索“2012世界末日”網(wǎng)頁地震撕裂效果代碼,引入第三方插件實現(xiàn)頁面的抖動、撕裂及圖片等效果,需要的朋友可以參考下
    2015-10-10
  • javascript中基本類型和引用類型的區(qū)別分析

    javascript中基本類型和引用類型的區(qū)別分析

    大多數(shù)人系統(tǒng)學習過的程序設計語言,在這些語言的學習過程中最早學到的幾個要點之一就是值類型和引用類型的區(qū)別。下面我們來看一下在 JavaScript 中基本數(shù)據(jù)類型(Primitive Types)和引用類型(Reference Types)的區(qū)別。
    2015-05-05
  • 通過babel操作AST精準插入配置代碼全流程

    通過babel操作AST精準插入配置代碼全流程

    這篇文章主要為大家介紹了通過babel操作AST精準插入配置代碼的全流程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步
    2022-02-02
  • JS將unicode碼轉中文方法

    JS將unicode碼轉中文方法

    本篇文章主要介紹了JS將unicode碼轉中文方法的相關知識,具有很好的參考價值。下面跟著小編一起來看下吧
    2017-05-05
  • JavaScript中split()方法舉例詳解

    JavaScript中split()方法舉例詳解

    這篇文章主要給大家介紹了關于JavaScript中split()方法的相關資料,split()方法在js處理字符串是很常見,也是很重要的一種方法必須熟練掌握,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2023-11-11

最新評論