JavaScript >> Javascript 文檔 >  >> JavaScript

JavaScript 閉包指南

簡介

閉包是 JavaScript 語言的一個有點抽象的概念,並潛入編程的編譯器端。但是,了解 JavaScript 如何解釋函數、嵌套函數、作用域和詞法環境對於充分發揮其潛力至關重要。

在本文中,我們將嘗試揭開上述概念的神秘面紗,並提供 JavaScript 閉包的簡單指南 .

什麼是閉包?

首先我們看一下MDN官方對閉包的定義:

簡單來說,閉包是一個可以訪問外部函數作用域的函數。為了理解這一點,讓我們看一下作用域在 JavaScript 中是如何工作的。

JavaScript 中的作用域

範圍 確定哪些變量是可見的或可以在給定的上下文中引用。作用域大致分為兩種——全局作用域本地範圍

  • 全球範圍 - 在函數外部定義的變量。這個範圍內的變量可以在程序的任何地方訪問和更改,因此得名“全局”。

  • 本地範圍 - 在函數內部定義的變量。這些變量特定於定義它們的函數,因此命名為“local”。

我們來看看 JavaScript 中的一個全局變量和局部變量:

let name = "Joe";

function hello(){
    let message = "Hello";
    console.log(message + " " +name);
}

在上面的例子中,name 的範圍 是全局的,即它可以在任何地方訪問。另一方面,message 在函數內部定義,其範圍是 hello() 的本地 功能。

JavaScript 使用詞法作用域 當涉及到功能範圍時。這意味著變量的範圍由其定義在源代碼中的位置定義。這讓我們可以在更小的範圍內引用全局變量。局部變量可以使用全局變量,反之則不行。

function outer(){
    let x = 10;
    
    function inner() {
        let y = 20;
        console.log(x);
    }
    
    inner();
    console.log(y)
}

outer();

此代碼導致:

10
error: Uncaught ReferenceError: y is not defined

inner() 函數可以引用x 因為它是在 outer() 中定義的 功能。但是,console.log(y) outer() 中的語句 函數不能引用 y 變量,因為它是在 inner() 中定義的 函數的作用域。

此外,在這種情況下:

let x = 10;

function func1(){
   console.log(x);
}

function func2() {
  let x = 20;
  func1();
}

func2();

輸出將是:

10

當我們調用 func1()func2() 內 ,我們有一個局部範圍的變量 x .但是,這個變量與 func1() 完全無關 因為它在 func1() 中無法訪問 .

因此,func1() 檢查是否存在具有該標識符的全局變量,並使用它,產生 10 的值 .

引擎蓋下的閉包

閉包是一個即使在外部函數返回後也可以訪問其父變量的函數。換句話說,一個閉包有三個作用域:

  • 本地範圍 - 訪問自己範圍內的變量
  • 父函數的範圍 - 訪問其父函數中的變量
  • 全局範圍 - 訪問全局變量

讓我們通過創建一個返回另一個函數的函數來看看閉包的工作原理:

function outer() {
    let x = 3
    return function inner(y) {
        return x*y
    }
}

let multiplyByThree = outer();

console.log(multiplyByThree(2));

這導致:

免費電子書:Git Essentials

查看我們的 Git 學習實踐指南,其中包含最佳實踐、行業認可的標準以及隨附的備忘單。停止谷歌搜索 Git 命令並真正學習 它!

6

如果我們這樣做:

console.log(multiplyByThree);

我們受到了歡迎:

function inner(y) { return x * y; }

讓我們逐步瀏覽代碼,看看幕後發生了什麼:

  1. outer() 函數在全局範圍內定義。
  2. outer() 被調用,它返回一個分配給 multiplyByThree 的函數 .
    1. outer() 創建新的執行上下文 .
      • 變量x 設置為 3。
    2. 返回一個名為 inner() 的函數 .
    3. inner() 的引用 分配給 multiplyByThree .
    4. 隨著外部函數執行完畢,其作用域內的所有變量都將被刪除。
  3. 函數調用結果multiplyByThree(2) 被記錄到控制台。
    1. inner() 使用 2 調用 作為論據。所以,y 設置為 2 .
    2. 作為 inner() 保留其父函數的作用域鏈,在執行時它仍然可以訪問 x 的值 .
    3. 返回 6 這會記錄到控制台。

可視化閉包

閉包可以通過開發者控制台進行可視化:

function outer() {
    let x = 3
    return function inner(y) {
        return x*y
    }
}

let multiplyByThree = outside();
console.dir(multiplyByThree);

通過在開發者控制台執行上面的代碼,我們可以看到我們可以訪問到 inner(y) 的上下文 .經過仔細檢查,我們可以看到它的上下文部分是 [[Scopes]] 數組,其中包含我們剛才討論的所有三個作用域。

瞧,範圍數組包含其父函數的範圍,其中包含 x = 3

常見用例

閉包很有用,因為它們可以幫助我們使用對數據進行操作的函數對數據進行聚類。這可能會給熟悉面向對象編程 (OOP) 的一些人敲響警鐘。因此,我們可以在任何可能使用對象的地方使用閉包。

閉包的另一個主要用例是當我們需要我們的變量是 private ,因為在閉包範圍內定義的變量對它之外的函數是禁止的。同時,閉包可以訪問其作用域鏈中的變量。

讓我們看下面的例子來更好地理解這一點:

const balance = (function() {
    let privateBalance = 0;

    return {
        increment: function(value){
            privateBalance += value;
            return privateBalance;
        },
        decrement: function(value){
            privateBalance -= value;
            return privateBalance;
        },
        show: function(){
            return privateBalance;
        }
    }
})()

console.log(balance.show()); // 0
console.log(balance.increment(500)); // 500
console.log(balance.decrement(200)); // 300

在這個例子中,我們定義了一個常量變量balance 並將其設置為我們匿名函數的返回值。注意 privateBalance 只能通過調用 balance 上的方法來更改 .

結論

儘管閉包在 JavaScript 中是一個相當小眾的概念,但它們是優秀 JavaScript 開發人員工具包中的重要工具。它們可以用來優雅地實現解決方案,否則這將是一項艱鉅的任務。

在本文中,我們首先了解了範圍以及它們是如何在 JavaScript 中實現的。然後,我們利用這些知識來了解閉包如何在幕後工作以及如何使用它們。


Tutorial JavaScript 教程
  1. 在 JavaScript 中按值傳遞

  2. Jquery .ajax 函數在已經跳到下一行代碼後返回對象的問題

  3. Javascript中的簡單鍊錶

  4. 當 React Hooks 剛剛在我腦海中點擊時💡🤩

  5. 幫我顯示網址

  6. 動態調度和調度表

  7. 厭倦了 Typescript/Node.js 中的循環依賴?

  1. JavaScript split() a String – String to Array JS 方法

  2. 贏得 HackFinity 2020 .. 感謝這個富有成果的項目

  3. 如何計算滾動條的寬度?

  4. JS 數字分隔符

  5. 我們在大約五分鐘的工作和十行代碼中將 vendor.js 從 210kb 減少到 16kb

  6. 使用 trackBy 提高 *ngFor 性能

  7. 有沒有辦法在 Selenium WebDriver 中使用 JavaScript 通過 XPath 獲取元素?

  1. 使用 github 包發布私有 npm 包

  2. 解決方案:從列表末尾刪除第 N 個節點

  3. 為 Web 優化 SVG 圖像

  4. 2019 年開始使用 TypeScript