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

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

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

1.什么是閉包:

閉包是JavaScript中最強(qiáng)大且獨(dú)特的特性之一,它是函數(shù)與其詞法環(huán)境的組合。閉包使得函數(shù)能夠訪問其外部作用域的變量,即使外部函數(shù)已經(jīng)執(zhí)行完畢。這種機(jī)制讓JavaScript具備了許多其他語言需要復(fù)雜語法才能實(shí)現(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. 核心概念與前置知識(shí):

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

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

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

// 偽代碼:執(zhí)行上下文的內(nèi)部結(jié)構(gòu)
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)境是存儲(chǔ)標(biāo)識(shí)符-變量映射的結(jié)構(gòu),由環(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); // 訪問多個(gè)作用域的變量
    }
    
    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)

作用域鏈?zhǔn)峭ㄟ^詞法環(huán)境的外部引用形成的鏈條,用于標(biāo)識(shí)符解析。

作用域鏈查找算法

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

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

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í)行過程分析

詳細(xì)步驟解析:

// 步驟 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變量因?yàn)楸婚]包引用而不會(huì)被垃圾回收
}

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

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

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

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

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

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

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

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} 被點(diǎn)擊了`);
    // 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)典錯(cuò)誤示例
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塊級(jí)作用域
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)存泄漏風(fēng)險(xiǎn)

// 可能導(dǎo)致內(nèi)存泄漏的代碼
function createHandler() {
  const largeData = new Array(1000000).fill('data'); // 大量數(shù)據(jù)
  
  return function() {
    // 即使不使用largeData,它也會(huì)被閉包保留
    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會(huì)被垃圾回收
  };
}

總結(jié) 

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

相關(guān)文章

最新評(píng)論

泰和县| 安陆市| 容城县| 衡阳县| 胶南市| 平山县| 远安县| 察哈| 囊谦县| 永吉县| 潼南县| 大同县| 百色市| 施秉县| 武乡县| 绍兴县| 重庆市| 甘德县| 肃南| 巫溪县| 紫阳县| 甘谷县| 霍山县| 新泰市| 新安县| 正镶白旗| 红河县| 苍山县| 灵丘县| 麻栗坡县| 东方市| 灯塔市| 南澳县| 陵川县| 禹城市| 德江县| 玉环县| 呼伦贝尔市| 洱源县| 光泽县| 青河县|