深入理解閉包

来源:https://www.cnblogs.com/Black-Skin/archive/2019/09/13/11456263.html
-Advertisement-
Play Games

前言: 對於大多數前端同學來說閉包一直是個很讓人困惑的問題,我自己之前雖說在項目中有意無意的用到但是都沒有刻意的去深入研究它,大部分時間是為了應付面試。後來某一天我突然意識到自己要去認真研究下它,因為知其然而不知其所以然並不應該是學習一種語言的態度,所以我打算寫篇文章嘗試著用我自己的理解去解釋下閉包 ...


前言:

       對於大多數前端同學來說閉包一直是個很讓人困惑的問題,我自己之前雖說在項目中有意無意的用到但是都沒有刻意的去深入研究它,大部分時間是為了應付面試。後來某一天我突然意識到自己要去認真研究下它,因為知其然而不知其所以然並不應該是學習一種語言的態度,所以我打算寫篇文章嘗試著用我自己的理解去解釋下閉包。

一、什麼是閉包?

      關於閉包不同的人有不同的理解,在javaScript高級編程第三版中給出瞭如下的解釋:閉包是指有權訪問另一個函數作用域中的變數的函數。其實解釋出來說閉包就是一個函數,只不過它可以訪問別的函數內部的變數。單純的看這句話很是抽象 ,我們先看一段代碼:

        function fn1() {
            var a = 1;
            function fn2() {
                console.log(a)
            }
        }
        fn1();

 

這段代碼中我們先聲明瞭一個函數fn1然後在函數內部生明瞭一個函數fn2而我們在fn2中引用了fn1中的變數a ;當我們執行fn1時,這時候閉包就產生了。我們可以藉助谷歌瀏覽器f12調試工具很清楚的看到閉包

在14行打斷點後我們很清楚的看到在內部函數f2內部有一個Closure的對象,這就是我們所說的閉包.此時我們對閉包可以更準確的的解釋:閉包是一個存在內部函數對象里的包含被引用變數的對象

二、如何產生閉包?

當一個嵌套的內部函數引用了外部函數的變數時,就產生了閉包。上例中 內部函數fn2引用了外部函數f1的內部變數a 因此就產生了閉包。

三、常見的產生閉包的方式

(1)、將函數作為另一個函數的返回值

        function add() {
            var num = 0;
            var fn2 = function() {
                num++;
                console.log(num)
            }
            return fn2
        }
        var fn = add();
        fn() //1
        fn() //2

由於內部函數對象fn2引用了外部函數add內的變數num,所以當add方法執行時就產生了閉包。

(2)、將一個函數當作實參傳入

        function showMsg(msg, time) {
            setTimeout(function() {
                alert(msg)
            }, time)
        }
        showMsg('hello', 2000)

由於內部匿名函數引用了外部函數showMsg的變數msg 因此產生了閉包

四、閉包的作用

我們接著使用之前的例子:

 function add() {
            var num = 0;
            var fn2 = function() {
                num++;
                console.log(num)
            }
            return fn2
        }
        var fn=add();
            fn()//1
            fn()//2

 (1)函數在執行完畢後依然保留內部變數

正常情況下add函數執行完成後,add函數內部的局部變數將會被垃圾回收機制回收,但當我們用fn保留對add內部的函數的引用時 內部函數對象就不會被釋放,而內部函數又引用了num,因此變數num在add函數執行完畢後也不會被釋放,而是繼續存在記憶體中。

(2)訪問函數內部變數

 由於js的機制導致我們訪問某一變數時我們只能從內往外部去訪問,但是使用閉包我們就可以訪問某函數內部的局部變數。從上例可以看到,我們在window中調用了add()函數,並且訪問到了add的局部變數 num 

五、閉包在實際開發中的應用

正如文章開頭所說,我們在實際的開發過程中總能有意無意的用到閉包,下麵我隨便列舉幾個例子,更好的幫大家理解閉包、

(1)、迴圈便利加監聽

  現在我有一個ul 內部4個li,要求點擊每個li的時候彈出其對應的下標,大家很容易想到如下寫法:

  let list = document.querySelectorAll('li');
        for (var i = 0; i < list.length; i++) {
            var el = list[i];
            el.index = i;
            el.onclick = function() {
                alert(el.index)
            }
        }

當我們執行的時候發現,點擊的時候彈出的都是3。這是因為i聲明的是一個全局變數,當我們加監聽之前迴圈已經執行完畢了這時候我們得到的i是3,所以每一個el.indexf賦的值都是一樣的,所以每次點擊都是3.我們現需要改進下代碼:

 let list = document.querySelectorAll('li');
        for (var i = 0; i < list.length; i++) {
            (function() {
                var el = list[i];
                el.index = i;
                el.onclick = function() {
                    alert(el.index)
                }
            })(i)
        }

我們在監聽事件外部 包了一個匿名的自執行函數,這時候我們發現都能得到我們期望的結果了。因為for迴圈每此執行時我們將i傳入了匿名函數並賦值給了el.index,而onclick的函數又引用了el.index這個變數,因此產生了閉包,每一個index都將被存下。 

 (2)、緩存this

   var name = 'Jony';
        var obj = {
            name: 'Tom',
            fn: function() {
                var _this = this;
                return function() {
                    return function() {
                        alert(_this.name)
                    }
                }
            }

        }
        obj.fn()()() //Tom

此例子中,我們obj.fn()得到了一個函數對象,並且此時得this是obj,我們把this賦值給了_this,因此我們執行最終得alert時得到得是Tom

(3)、封裝JS模塊

我們為了防止變數被污染經常回去使用閉包去封裝JS 模塊

  (function(window) {
            var msg = "Hello"; //私有變數
            function fn() {
                return msg.toLowerCase()
            }

            function fn2() {
                return msg.split('');
            }
            window.module = {
                fn: fn,
                fn2: fn2
            }
        })(window)

以上就是我個人對於閉包的理解,希望能幫助到正咋子為閉包而困惑的你.

如有錯誤敬請指出!!,與諸君共勉!!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.確保系統中有依賴的libaio 軟體,如果沒有: yum -y install libaio 2.解壓二進位MySQL軟體包 tar xf mysql-5.7.24-linux-glibc2.12-x86_64.tar.gz -C /usr/local 3.進入/usr/local cd /us ...
  • “處理指令(PIs)允許文檔包含用於應用程式的指令。指令並不是文檔字元數據的一部分,但是必須通過應用程式傳遞”。 處理指令可以用於將信息傳遞給應用程式。處理指令可以出現在文檔任意位置的標記外部。可以出現在序言中,包括文檔的類型定義(DTD),文本內容或者文檔之後。 處理指令,允許文檔中包含由應用程式 ...
  • 數據交互是前端很重要的一部分,靜態頁是基礎,而交互才是網頁的精髓。交互又分為人機交互和前後端數據交互,現階段的互聯網下,大部分的網站都要進行前後端數據交互,如何交互呢?交互的流程大概就是前端發送數據給後端,後端接送數據,進行處理,將處理後的結果發送給前端,前端接受數據。前端和後端的收和發通過什麼呢? ...
  • 結構元素不具有任何樣式,只是使頁面元素的的語義更加明確。 header元素 header元素是一種具有引導和導航作用的的結構元素, 該元素可以包含所有通常放在頁面頭部的內容 。header元素通常用來放置整個頁面或頁面內的一個內容區塊的標題,也可以包含網站Logo圖片、搜索表單或者其他相關內容。 一 ...
  • js由三部分組成,分別是ECMAScript、DOM、BOM 其中ECMAScript規定了js的語法 js是一門解釋型語言、腳本語言、動態類型語言、基於對象語言 書寫js代碼和CSS一樣,有三個書寫的地方,第一個是使用<script>標簽,再<sccript>標簽中書寫js代碼,標簽一般都在bod ...
  • 如果您的瀏覽器支持 XSLT,那麼在瀏覽器中它可被用來將文檔轉換為 XHTML。 如果您的瀏覽器支持 XSLT,那麼在瀏覽器中它可被用來將文檔轉換為 XHTML。 JavaScript 解決方案 在前面的章節,我們已向您講解如何使用 XSLT 將某個 XML 文檔轉換為 XHTML。我們是通過以下途 ...
  • 一、html語言概述 超文本標記語言(英語:HyperText Markup Language,簡稱:HTML)是一種用於創建網頁的標準標記語言。 您可以使用 HTML 來建立自己的 WEB 站點,HTML 運行在瀏覽器上,由瀏覽器來解析。 二、目前瀏覽器內核(瞭解): 三、HTML 初體驗 上述實 ...
  • ​ 路由獨立守衛,顧名思義就是這個路由自己的守衛任務,就如同咱們LOL,我們守衛的就是獨立一條路,保證我們這條路不要被敵人攻剋(當然我們也得打團配合) 在官方定義是這樣說的:你可以在路由配置上直接定義 beforeEnter 守衛,這些守衛與全局前置守衛的方法參數是一樣的。 參數如下: 我們在這裡使 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...