淺談js作用域

每個JavaScript函數都是一個對象,對象中有屬性,可以訪問如:

        function text(){}
        console.log(text.name) // text

還有一些屬性是我們訪問不了的如:

        function text(){}
        console.log(text.[[scope]])  //報錯

[[scope]]是函數的屬性,但是是一種隱式的屬性,我們訪問不了,而他代表的就是函數產生的作用域,存儲了執行期上下文的集合。它既然用不了,那它還有作用嗎?有的雖然我們用不了,但是系統是可以使用的(只許州官放火不許百姓點燈)。

執行期上下文:當函數執行前,會創建一個執行期上下文的對象,執行期上下文定義了一個函數執行時的環境(我也不知道該怎麼解釋這句,就是這個函數會依賴這個環境去執行,,,大家試着去理解就可以),函數每次執行時對應的執行期上下文都是獨一無二的,所以多次調用一個函數會產生多個執行期上下文,但是這麼多執行期上下文放着也沒用,還佔內存,所以函數執行完畢以後就會銷燬它自己產生的執行期上下文。

        function text(){}  //函數創建產生AO{}
        text();           // 函數執行 銷燬AO對象,盡顯渣男本色

作用域鏈: [[scope]]中所存的執行期上下文對象的集合,這個集合成鏈式鏈接,我們把這中鏈式鏈接叫做作用域鏈。
好了看天書環節過去了,可以看代碼說問題了:

        var c = 10;
        function a(){  // 在a函數剛剛被定義的時候,它就有了自己的[[scope]]屬性 裏面第一位放着全局對象GO

            console.log(c) // 10  ,在AO裏找不到c變量,就到GO裏找到c 打印10

            function b(){ //在b函數剛剛被定義的時候,它就有了自己的[[scope]]屬性 裏面第一位放着a函數創建時的AO,第二位放着全局對象GO

                var b = 234;

            }

            var a = 123;

            b();//函數執行時在[[scope]]屬性裏面把自己創建的AO放在作用域鏈的頂端,AO和GO向下移一位。
        }
        var glob = 100;
        a();   //函數執行時在[[scope]]屬性裏面把自己創建的AO放在作用域鏈的頂端,GO向下移一位。
		a,b函數定義和執行時他們能訪問到的作用域及訪問先後順序
         a 定義  a.[[scope]]  ---------------- 0 : GO
         
         a 執行  a.[[scope]]  ---------------- 0 : aAO
                                               1 : GO
                                               
         b 定義  b.[[scope]]  ---------------- 0 : aAO
                                               1 : Go
                                               
         b 執行  b.[[scope]]  ---------------- 0 : bAO
                                               1 : aAO
                                               2 : GO   

函數在執行時,會產生AO,當它訪問變量時,先去找AO,再去找GO,這其實說明這樣他們之間有一種聯繫,即作用域鏈。而自己創建的AO永遠放在作用域鏈的頂端。
最後當b函數執行完畢,b函數銷燬自己的AO對象,保留a函數的AO和全局的GO,處於定義階段,等待執行,截止a函數也執行完畢時,a函數銷燬自己的AO對象,保留全局GO對象,等待執行,但是注意一點,a函數的AO對象中,是有b函數的定義的,a函數的AO也保留在b函數的定義中,所以a函數的AO沒有被真正意義上的銷燬。

還有一點:a函數執行創建一個自己的AO,內部的b函數也可以訪問這個AO,哪兩個AO有什麼關係呢?是複製了一個,還是大家用的同一個呢?看代碼:

   function a() {
        var cc = 0;
        function b() {
            cc = 10;
        }
        b();
        console.log(cc) // 10 打印出了10,這說明b函數訪問的AO對象和a函數創建的AO對象是同一個。
    }
    a();     

入職不久的小前端從今以後開始自己的技術分享之旅,歡迎建議和批評。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章