深入理解閉包

来源: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
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...