<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
        當前位置: 首頁 - 科技 - 知識百科 - 正文

        前端進階(六):觀察函數調用棧、作用域鏈與閉包

        來源:懂視網 責編:小采 時間:2020-11-27 20:23:09
        文檔

        前端進階(六):觀察函數調用棧、作用域鏈與閉包

        前端進階(六):觀察函數調用棧、作用域鏈與閉包:配圖與本文無關在前端開發中,有一個非常重要的技能,叫做斷點調試。在chrome的開發者工具中,通過斷點調試,我們能夠非常方便的一步一步的觀察JavaScript的執行過程,直觀感知函數調用棧,作用域鏈,變量對象,閉包,this等關鍵信息的變化。因此,斷點調試對
        推薦度:
        導讀前端進階(六):觀察函數調用棧、作用域鏈與閉包:配圖與本文無關在前端開發中,有一個非常重要的技能,叫做斷點調試。在chrome的開發者工具中,通過斷點調試,我們能夠非常方便的一步一步的觀察JavaScript的執行過程,直觀感知函數調用棧,作用域鏈,變量對象,閉包,this等關鍵信息的變化。因此,斷點調試對
        一、基礎概念回顧

        函數在被調用執行時,會創建一個當前函數的執行上下文。在該執行上下文的創建階段,變量對象、作用域鏈、閉包、this指向會分別被確定。而一個JavaScript程序中一般來說會有多個函數,JavaScript引擎使用函數調用棧來管理這些函數的調用順序。函數調用棧的調用順序與棧數據結構一致。

        二、認識斷點調試工具

        在盡量新版本的chrome瀏覽器中(不確定你用的老版本與我的一致),調出chrome瀏覽器的開發者工具。

        瀏覽器右上角豎著的三點 -> 更多工具 -> 開發者工具 -> Sources

        界面如圖。


        斷點調試界面

        在我的demo中,我把代碼放在app.js中,在index.html中引入。我們暫時只需要關注截圖中紅色箭頭的地方。在最右側上方,有一排圖標。我們可以通過使用他們來控制函數的執行順序。從左到右他們依次是:

      1. resume/pause script execution
        恢復/暫停腳本執行

      2. step over next function call
        跨過,實際表現是不遇到函數時,執行下一步。遇到函數時,不進入函數直接執行下一步。

      3. step into next function call
        跨入,實際表現是不遇到函數時,執行下一步。遇到到函數時,進入函數執行上下文。

      4. step out of current function
        跳出當前函數

      5. deactivate breakpoints
        停用斷點

      6. don‘t pause on exceptions
        不暫停異常捕獲

      7. 其中跨過,跨入,跳出是我使用最多的三個操作。

        上圖右側第二個紅色箭頭指向的是函數調用棧(call Stack),這里會顯示代碼執行過程中,調用棧的變化。

        右側第三個紅色箭頭指向的是作用域鏈(Scope),這里會顯示當前函數的作用域鏈。其中Local表示當前的局部變量對象,Closure表示當前作用域鏈中的閉包。借助此處的作用域鏈展示,我們可以很直觀的判斷出一個例子中,到底誰是閉包,對于閉包的深入了解具有非常重要的幫助作用。

        三、斷點設置

        在顯示代碼行數的地方點擊,即可設置一個斷點。斷點設置有以下幾個特點:

      8. 在單獨的變量聲明(如果沒有賦值),函數聲明的那一行,無法設置斷點。

      9. 設置斷點后刷新頁面,JavaScript代碼會執行到斷點位置處暫停執行,然后我們就可以使用上邊介紹過的幾個操作開始調試了。

      10. 當你設置多個斷點時,chrome工具會自動判斷從最早執行的那個斷點開始執行,因此我一般都是設置一個斷點就行了。

      11. 四、實例

        接下來,我們借助一些實例,來使用斷點調試工具,看一看,我們的demo函數,在執行過程中的具體表現。

        // demo01
        
        var fn;
        function foo() {
         var a = 2;
         function baz() { 
         console.log( a );
         }
         fn = baz; 
        }
        function bar() {
         fn(); 
        }
        
        foo();
        bar(); // 2

        在向下閱讀之前,我們可以停下來思考一下,這個例子中,誰是閉包?

        這是來自《你不知道的js》中的一個例子。由于在使用斷點調試過程中,發現chrome瀏覽器理解的閉包與該例子中所理解的閉包不太一致,因此專門挑出來,供大家參考。我個人更加傾向于chrome中的理解。

      12. 第一步:設置斷點,然后刷新頁面。


      13. 設置斷點

      14. 第二步:點擊上圖紅色箭頭指向的按鈕(step into),該按鈕的作用會根據代碼執行順序,一步一步向下執行。在點擊的過程中,我們要注意觀察下方call stack 與 scope的變化,以及函數執行位置的變化。

      15. 一步一步執行,當函數執行到上例子中


        baz函數被調用執行,foo形成了閉包

        我們可以看到,在chrome工具的理解中,由于在foo內部聲明的baz函數在調用時訪問了它的變量a,因此foo成為了閉包。這好像和我們學習到的知識不太一樣。我們來看看在《你不知道的js》這本書中的例子中的理解。


        你不知道的js中的例子

        書中的注釋可以明顯的看出,作者認為fn為閉包。即baz,這和chrome工具中明顯是不一樣的。

        而在備受大家推崇的《JavaScript高級編程》一書中,是這樣定義閉包。


        JavaScript高級編程中閉包的定義


        書中作者將自己理解的閉包與包含函數所區分

        這里chrome中理解的閉包,與我所閱讀的這幾本書中的理解的閉包不一樣。具體這里我先不下結論,但是我心中更加偏向于相信chrome瀏覽器。

        我們修改一下demo01中的例子,來看看一個非常有意思的變化。

        // demo02
        var fn;
        var m = 20;
        function foo() {
         var a = 2;
         function baz(a) { 
         console.log(a);
         }
         fn = baz; 
        }
        function bar() {
         fn(m); 
        }
        
        foo();
        bar(); // 20

        這個例子在demo01的基礎上,我在baz函數中傳入一個參數,并打印出來。在調用時,我將全局的變量m傳入。輸出結果變為20。在使用斷點調試看看作用域鏈。


        閉包沒了,作用域鏈中沒有包含foo了。

        是不是結果有點意外,閉包沒了,作用域鏈中沒有包含foo了。我靠,跟我們理解的好像又有點不一樣。所以通過這個對比,我們可以確定閉包的形成需要兩個條件。

      16. 在函數內部創建新的函數;

      17. 新的函數在執行時,訪問了函數的變量對象;

      18. 還有更有意思的。

        我們繼續來看看一個例子。

        // demo03
        
        function foo() {
         var a = 2;
        
         return function bar() {
         var b = 9;
        
         return function fn() {
         console.log(a);
         }
         }
        }
        
        var bar = foo();
        var fn = bar();
        fn();

        在這個例子中,fn只訪問了foo中的a變量,因此它的閉包只有foo。


        閉包只有foo

        修改一下demo03,我們在fn中也訪問bar中b變量試試看。

        // demo04
        
        function foo() {
         var a = 2;
        
         return function bar() {
         var b = 9;
        
         return function fn() {
         console.log(a, b);
         }
         }
        }
        
        var bar = foo();
        var fn = bar();
        fn();


        這個時候閉包變成了兩個

        這個時候,閉包變成了兩個。分別是bar,foo。

        我們知道,閉包在模塊中的應用非常重要。因此,我們來一個模塊的例子,也用斷點工具來觀察一下。

        // demo05
        (function() {
        
         var a = 10;
         var b = 20;
        
         var test = {
         m: 20,
         add: function(x) {
         return a + x;
         },
         sum: function() {
         return a + b + this.m;
         },
         mark: function(k, j) {
         return k + j;
         }
         }
        
         window.test = test;
        
        })();
        
        test.add(100);
        test.sum();
        test.mark();
        
        var _mark = test.mark;
        _mark();


        add執行時,閉包為外層的自執行函數,this指向test


        sum執行時,同上


        mark執行時,閉包為外層的自執行函數,this指向test


        _mark執行時,閉包為外層的自執行函數,this指向window

        注意:這里的this指向顯示為Object或者Window,大寫開頭,他們表示的是實例的構造函數,實際上this是指向的具體實例

        test.mark能形成閉包,跟下面的補充例子(demo07)情況是一樣的。

        我們還可以結合點斷調試的方式,來理解那些困擾我們很久的this指向。隨時觀察this的指向,在實際開發調試中非常有用。

        // demo06
        
        var a = 10;
        var obj = {
         a: 20
        }
        
        function fn () {
         console.log(this.a);
        }
        
        fn.call(obj); // 20


        this指向obj

        補充一個例子

        // demo07
        function foo() {
         var a = 10;
        
         function fn1() {
         return a;
         }
        
         function fn2() {
         return 10;
         }
        
         fn2();
        }
        
        foo();

        這個例子,和其他例子不太一樣。雖然fn2并沒有訪問到foo的變量,但是foo執行時仍然變成了閉包。而當我將fn1的聲明去掉時,閉包便不會出現了。我暫時也不知道應該如何解釋這種情況。只能大概知道與fn1有關,可能瀏覽器在實現時就認為只要存在訪問上層作用域的可能性,就會被當成一個閉包吧。所以暫時就只能將它作為一個特例記住。

        更多的例子,大家可以自行嘗試,總之,學會了使用斷點調試之后,我們就能夠很輕松的了解一段代碼的執行過程了。這對快速定位錯誤,快速了解他人的代碼都有非常巨大的幫助。大家一定要動手實踐,把它給學會。

        最后,根據以上的摸索情況,再次總結一下閉包:

      19. 閉包是在函數被調用執行的時候才被確認創建的。

      20. 閉包的形成,與作用域鏈的訪問順序有直接關系。

      21. 只有內部函數訪問了上層作用域鏈中的變量對象時,才會形成閉包,因此,我們可以利用閉包來訪問函數內部的變量。

      22. chrome中理解的閉包,與《你不知道的js》與《JavaScript高級編程》中的閉包理解有很大不同,我個人更加傾向于相信chrome。這里就不妄下結論了,大家可以根據我的思路,探索后自行確認。在之前一篇文中我根據從書中學到的下了定義,應該是錯了,目前已經修改,對不起大家了。

      23. 大家也可以根據我提供的這個方法,對其他的例子進行更多的測試,如果發現我的結論有不對的地方,歡迎指出,大家相互學習進步,謝謝大家。

        聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        前端進階(六):觀察函數調用棧、作用域鏈與閉包

        前端進階(六):觀察函數調用棧、作用域鏈與閉包:配圖與本文無關在前端開發中,有一個非常重要的技能,叫做斷點調試。在chrome的開發者工具中,通過斷點調試,我們能夠非常方便的一步一步的觀察JavaScript的執行過程,直觀感知函數調用棧,作用域鏈,變量對象,閉包,this等關鍵信息的變化。因此,斷點調試對
        推薦度:
        標簽: 作用 進階 ):
        • 熱門焦點

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 久久亚洲国产成人影院| 亚洲午夜在线一区| 麻豆成人精品国产免费| 免费观看成人毛片a片2008| 免费一级黄色毛片| 亚洲成在人线电影天堂色| 99在线观看免费视频| 91亚洲va在线天线va天堂va国产| 在线观看特色大片免费视频| 亚洲人成依人成综合网| 99久久久国产精品免费牛牛四川| **一级一级毛片免费观看| 亚洲AⅤ无码一区二区三区在线 | xxxxx做受大片在线观看免费| 成人精品一区二区三区不卡免费看| 亚洲午夜无码久久| 搡女人真爽免费视频大全| 日韩亚洲人成在线综合日本| 日本在线免费播放| 亚洲国产精品碰碰| 成人精品视频99在线观看免费| 永久免费av无码网站韩国毛片| 亚洲精品和日本精品| xxxxxx日本处大片免费看| 亚洲AV无码乱码在线观看裸奔| 亚洲va久久久噜噜噜久久| 亚洲人成在线播放| 免费a级毛片视频| 免费在线看黄的网站| 亚洲国产一区二区三区| 国产免费一级高清淫曰本片| 亚洲AV成人精品网站在线播放| 亚洲国产成人91精品| 日韩免费视频观看| 色噜噜亚洲男人的天堂| 国产成人精品高清免费| 亚洲精品无码久久久久牙蜜区| 国产免费人成视频尤勿视频| 亚洲福利在线视频| 国产免费av一区二区三区| 在线看片免费人成视频播 |