国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

前端基礎進階(六):在chrome開發者工具中觀察函數調用棧、作用域鏈與閉包

draveness / 1571人閱讀

摘要:在的開發者工具中,通過斷點調試,我們能夠非常方便的一步一步的觀察的執行過程,直觀感知函數調用棧,作用域鏈,變量對象,閉包,等關鍵信息的變化。其中表示當前的局部變量對象,表示當前作用域鏈中的閉包。

在前端開發中,有一個非常重要的技能,叫做斷點調試

在chrome的開發者工具中,通過斷點調試,我們能夠非常方便的一步一步的觀察JavaScript的執行過程,直觀感知函數調用棧,作用域鏈,變量對象,閉包,this等關鍵信息的變化。因此,斷點調試對于快速定位代碼錯誤,快速了解代碼的執行過程有著非常重要的作用,這也是我們前端開發者必不可少的一個高級技能。

當然如果你對JavaScript的這些基礎概念(執行上下文,變量對象,閉包,this等)了解還不夠的話,想要透徹掌握斷點調試可能會有一些困難。但是好在在前面幾篇文章,我都對這些概念進行了詳細的概述,因此要掌握這個技能,對大家來說,應該是比較輕松的。

這篇文章的主要目的在于借助對于斷點調試的學習,來進一步加深對閉包的理解。

一、基礎概念回顧

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

二、認識斷點調試工具

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

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

界面如圖。

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

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

step over next function call

跨過,實際表現是不遇到函數時,執行下一步。遇到函數時,不進入函數直接執行下一步。

step into next function call

跨入,實際表現是不遇到函數時,執行下一步。遇到到函數時,進入函數執行上下文。

step out of current function

跳出當前函數

deactivate breakpoints

停用斷點

don‘t pause on exceptions

不暫停異常捕獲

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

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

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

三、斷點設置

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

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

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

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

四、實例

接下來,我們借助一些實例,來使用斷點調試工具,看一看,我們的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中的理解。

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

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

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

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

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

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

這里chrome中理解的閉包,與我所閱讀的這幾本書中的理解的閉包不一樣。其實在之前對于閉包分析的文章中,我已經有對這種情況做了一個解讀。閉包詳解

閉包是一個特殊對象,它由執行上下文(代號A)與在該執行上下文中創建的函數(代號B)共同組成。

當B執行時,如果訪問了A中變量對象中的值,那么閉包就會產生。

那么在大多數理解中,包括許多著名的書籍,文章里都以函數B的名字代指這里生成的閉包。而在chrome中,則以執行上下文A的函數名代指閉包。

我們修改一下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了。我靠,跟我們理解的好像又有點不一樣。所以通過這個對比,我們可以確定閉包的形成需要兩個條件。

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

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

還有更有意思的。

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

// 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。

修改一下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();

注意:這里的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

最后繼續補充一個例子。

// demo07
function foo() {
    var a = 10;

    function fn1() {
        return a;
    }

    function fn2() {
        return 10;
    }

    fn2();
}

foo();

這個例子,和其他例子不太一樣。雖然fn2并沒有訪問到foo的變量,但是foo執行時仍然變成了閉包。而當我將fn1的聲明去掉時,閉包便不會出現了。

那么結合這個特殊的例子,我們可以這樣這樣定義閉包。

閉包是指這樣的作用域(foo),它包含有一個函數(fn1),這個函數(fn1)可以調用被這個作用域所封閉的變量(a)、函數、或者閉包等內容。通常我們通過閉包所對應的函數來獲得對閉包的訪問。

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

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

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

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

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

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

前端基礎進階系列目錄

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/90549.html

相關文章

  • 前端基礎進階(四):詳細圖解作用鏈與閉包

    摘要:之前一篇文章我們詳細說明了變量對象,而這里,我們將詳細說明作用域鏈。而的作用域鏈,則同時包含了這三個變量對象,所以的執行上下文可如下表示。下圖展示了閉包的作用域鏈。其中為當前的函數調用棧,為當前正在被執行的函數的作用域鏈,為當前的局部變量。 showImg(https://segmentfault.com/img/remote/1460000008329355);初學JavaScrip...

    aikin 評論0 收藏0
  • 前端基礎進階目錄

    摘要:不過其實簡書文章評論里有很多大家的問題以及解答,對于進一步理解文中知識幫助很大的,算是有點可惜吧。不過也希望能夠對正在學習前端的你有一些小幫助。如果在閱讀中發現了一些錯誤,請在評論里告訴我,我會及時更改。 前端基礎進階(一):內存空間詳細圖解 前端基礎進階(二):執行上下文詳細圖解 前端基礎進階(三):變量對象詳解 前端基礎進階(四):詳細圖解作用域鏈與閉包 前端基礎進階(五):全方位...

    mo0n1andin 評論0 收藏0
  • 文章分享(持續更新)

    摘要:文章分享持續更新更多資源請文章轉自一前端文章基礎篇,,前端基礎進階一內存空間詳細圖解前端基礎進階二執行上下文詳細圖解前端基礎進階三變量對象詳解前端基礎進階四詳細圖解作用域鏈與閉包前端基礎進階五全方位解讀前端基礎進階六在開發者工具中觀察函數調 文章分享(持續更新) 更多資源請Star:https://github.com/maidishike... 文章轉自:https://gith...

    whlong 評論0 收藏0
  • 小技巧 - 收藏集 - 掘金

    摘要:然而學習布局,你只要學習幾個手機端頁面自適應解決方案布局進階版附源碼示例前端掘金一年前筆者寫了一篇手機端頁面自適應解決方案布局,意外受到很多朋友的關注和喜歡。 十分鐘學會 Fiddler - 后端 - 掘金一.Fiddler介紹 Fiddler是一個http抓包改包工具,fiddle英文中有欺騙、偽造之意,與wireshark相比它更輕量級,上手簡單,因為只能抓http和https數據...

    A Loity 評論0 收藏0
  • 原理解釋 - 收藏集 - 掘金

    摘要:巧前端基礎進階全方位解讀前端掘金我們在學習的過程中,由于對一些概念理解得不是很清楚,但是又想要通過一些方式把它記下來,于是就很容易草率的給這些概念定下一些方便自己記憶的有偏差的結論。 計算機程序的思維邏輯 (83) - 并發總結 - 掘金從65節到82節,我們用了18篇文章討論并發,本節進行簡要總結。 多線程開發有兩個核心問題,一個是競爭,另一個是協作。競爭會出現線程安全問題,所以,本...

    AlphaGooo 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<