<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ## 閉包,是真的美 > 摘自 https://github.com/prettyEcho/deep-js/issues/4 > 作者:張建成,有修改 寫這篇文章時的心情是十分忐忑的,因為對于我們今天的主角:閉包,很多小伙伴都寫過關于它的文章,相信大家也讀過不少,那些文章到底有沒有把JS中這個近似神話的東西講清楚,說實心里,真的有,但為數不多。 寫這篇文章的初衷:讓所有看到這篇文章的小伙伴都徹徹底底的理解閉包 => 提高JS水平 => 能夠寫出更高質量的JS代碼。 開文之所以說心情是忐忑的,就是怕達不到我寫該文的初衷,但是我有信心同時我也會努力的完成我的目標。如行文中有絲毫誤人子弟的陳述,歡迎大家指正,在這感激不盡。 我們開始吧: 相信眾多JS的lovers都聽說過這句話:**閉包很重要但是很難理解**。 我起初也是這么覺得,但是當我努力學習了JS的一些深層的原理以后我倒覺得閉包并不是那么不好理解,反倒是讓我感覺出一種很美的感覺。當我徹底理解閉包的那一剎那,心中油然產生一種十分愉悅感覺,就像 **"酒酣尚醉,花未全開"** 那種美景一樣。 **撥開閉包神秘的面紗** 我們先看一個閉包的例子: ```js function foo() { let a = 2; function bar() { console.log( a ); } return bar; } let baz = foo(); baz(); ``` 大家肯定都寫過類似的代碼,相信很多小伙伴也知道這段代碼應用了閉包,but, Why does the closure be generated and Where is closure? 來,我們慢慢分析: 首先必須先知道閉包是什么,才能分析出閉包為什么產生和閉包到底在哪? 當一個函數能夠記住并訪問到其所在的詞法作用域及作用域鏈,特別強調是在其定義的作用域外進行的訪問,此時該函數和其上層執行上下文共同構成閉包。 需要明確的幾點: 1. 閉包一定是函數對象([wintercn](https://github.com/wintercn/blog/issues/3) 大大的閉包考證) 2. 閉包和詞法作用域,作用域鏈,垃圾回收機制息息相關 3. 當函數一定是在其定義的作用域外進行的訪問時,才產生閉包 4. 閉包是由該函數和其上層執行上下文共同構成(這點稍后我會說明) 閉包是什么,我們說清楚了,下面我們看下閉包是如何產生的。 接下來,我默認你已經讀過我之前的兩篇文章 [原來JavaScript內部是這樣運行的](https://github.com/prettyEcho/deep-js/issues/1) 和 [徹底搞懂JavaScript作用域](https://github.com/prettyEcho/deep-js/issues/3) , 建議先進行閱讀理解JS執行機制和作用域等相關知識,再理解閉包,否則可能會理解的不透徹。 現在我假設JS引擎執行到這行代碼 > let baz = foo(); 此時,JS的作用域氣泡是這樣的: ![img](http://xiaoyulive.oss-cn-beijing.aliyuncs.com/date/2018-04-12/38412730-97a3fd6e-39bc-11e8-9a53-208d71ca98eb.png) 這個時候foo函數已經執行完,JS的垃圾回收機制應該會自動將其標記為"離開環境",等待回收機制下次執行,將其內存進行釋放(標記清除)。 但是,我們仔細看圖中粉色的箭頭,我們將bar的引用指向baz,正是這種引用賦值,阻止了垃圾回收機制將foo進行回收,從而導致bar的整條作用域鏈都被保存下來。 接下來,baz() 執行,bar 進入執行棧,閉包(foo)形成,此時bar中依舊可以訪問到其父作用域氣泡中的變量 a。 這樣說可能不是很清晰,接下來我們借助chrome的調試工具看下閉包產生的過程。 當JS引擎執行到這行代碼 `let baz = foo();` 時: ![img](http://xiaoyulive.oss-cn-beijing.aliyuncs.com/date/2018-04-12/QQ%E6%88%AA%E5%9B%BE20180412141505.png) 圖中所示,`let baz = foo();` 已經執行完,即將執行baz();,此時Call Stack中只有全局上下文。 接下來baz();執行: ![img](http://xiaoyulive.oss-cn-beijing.aliyuncs.com/date/2018-04-12/QQ%E6%88%AA%E5%9B%BE20180412141811.png) 我們可以看到,此時bar進入Call Stack中,并且Closure(foo)形成。 針對上面我提到的幾點進行下說明: 1. 上述第二點(閉包和詞法作用域,作用域鏈,垃圾回收機制息息相關)大家應該都清楚了 2. 上述第三點,當函數baz執行時,閉包才生成 3. 上述第四點,閉包是foo,并不是bar,很多書(《you dont know JavaScript》《JavaScript高級程序設計》)中,都強調保存下來的引用,即上例中的bar是閉包,而chrome認為被保存下來的封閉空間foo是閉包,針對這點我贊同chrome的判斷(僅為自己的理解,如有不同意見,歡迎來討論) **閉包的藝術性** 我相信這個世界上最美的事物往往就存在我們身邊,通常它并不是那么神秘,那么不可見,只是我們缺少了一雙發現美的眼睛。 生活中,我們抽出一段時間放慢腳步,細細品味我們所過的每一分每一秒,會收獲到生活給我們的另一層樂趣。 閉包也一樣,它不是很神秘,反而是在我們的程序中隨處可見,當我們靜下心來,品味閉包的味道,發現它散發出一種藝術的美,樸實、精巧又不失優雅。 ![img](http://xiaoyulive.oss-cn-beijing.aliyuncs.com/date/2018-04-12/38449808-25cd072c-3a47-11e8-9fce-d122cdd788fe.jpg) 細想,在我們作用域氣泡模型中,作用域鏈讓我們的內部bar氣泡能夠"看到"外面的世界,而閉包則讓我們的外部作用域能夠"關注到"內部的情況成為可能。可見,只要我們愿意,**內心世界和外面世界是可以相通的**。 **閉包的應用的注意事項** 閉包,在JS中絕對是一個高貴的存在,它讓很多不可能實現的代碼成為可能,但是物雖好,也要合理使用,不然不但不能達到我們想要的效果,有的時候可能還會適得其反。 **內存泄漏(Memory Leak)** JavaScript分配給Web瀏覽器的可用內存數量通常比分配給桌面應用程序的少,這樣做主要是防止JavaScript的網頁耗盡全部系統內存而導致系統崩潰。 因此,要想使頁面具有更好的性能,就必須確保頁面占用最少的內存資源,也就是說,我們應該保證執行代碼只保存有用的數據,一旦數據不再有用,我們就應該讓垃圾回收機制對其進行回收,釋放內存。 我們現在都知道了閉包阻止了垃圾回收機制對變量進行回收,因此變量會永遠存在內存中,即使當變量不再被使用時,這樣會造成內存泄漏,會嚴重影響頁面的性能。因此當變量對象不再適用時,我們要將其釋放。 我們拿上面代碼舉例: ```js function foo() { let a = 2; function bar() { console.log( a ); } return bar; } let baz = foo(); baz(); // baz指向的對象會永遠存在堆內存中 baz = null; // 如果baz不再使用,將其指向的對象釋放 ``` 關于內存泄漏,推薦 [阮一峰老師博客](http://www.ruanyifeng.com/blog/2017/04/memory-leak.html)。 **閉包的應用** 1. 模塊 一個模塊應該具有私有屬性、私有方法和公有屬性、公有方法。 而閉包能很好的將模塊的公有屬性、方法暴露出來。 ```js var myModule = (function (window, undefined) { let name = "echo"; function getName() { return name; } return { name, getName } })(window); console.log( myModule.name ); // echo console.log( myModule.getName() ); // echo ``` "return" 關鍵字將對象引用導出賦值給 myModule,從而應用到閉包。 2. 延時器(setTimeout)、計數器(setInterval) 這里簡單寫一個常見的關于閉包的面試題。 ```js for( var i = 0; i < 5; i++ ) { setTimeout(() => { console.log( i ); }, 1000 * i) } ``` 答案大家都知道:**每秒鐘輸出一個 5,一共輸出 5 次**。 那么如何做到**每秒鐘輸出一個數,以此為 0,1,2,3,4** 呢? 我們這里只介紹 **閉包的解決方法** ,其他類似塊作用域等等的解決方法,我們這里不討論。 ```js for( var i = 0; i < 5; i++ ) { ((j) => { setTimeout(() => { console.log( j ); }, 1000 * j) })(i) } ``` "setTimeout" 方法里應用了閉包,使其內部能夠記住每次循環所在的詞法作用域和作用域鏈。 由于setTimeout中的回調函數會在當前任務隊列的尾部進行執行,因此上面第一個例子中每次循環中的setTimeout回調函數記住的i的值是for循環作用域中的值,此時都是5,而第二個例子記住的i的數為setTimeout的父級作用域自執行函數中的j的值,依次為0,1,2,3,4。 3. 監聽器 ```js var oDiv = document.querySeletor("#div"); oDiv.addEventListener('click', function() { console.log( oDiv.id ); }) ``` 如果在循環點擊中 : ```js var divs = document.querySelectorAll('div') for (var i = 0; i < divs.length; i++) { divs[i].addEventListener('click', () => { console.log(i) }) } ``` 這樣的話,不管點擊任何 div 元素,輸出的都是 5,要解決此問題,思路仍然是閉包 : ```js var divs = document.querySelectorAll('div') for (var i = 0; i < divs.length; i++) { ((i) => { divs[i].addEventListener('click', () => { console.log(i) }) })(i) } ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看