1. 程式人生 > >第四章 作用域和記憶體問題 第二節執行環境及作用域

第四章 作用域和記憶體問題 第二節執行環境及作用域

1、執行環境的型別有什麼?
2、全域性執行環境是怎麼樣的?
3、全域性執行環境是何時銷燬?
4、程式中的執行流是由什麼機制控制的?
5、什麼是作用域鏈?
6、作用域鏈的用途是什麼?
7、識別符號是如何解析的?
8、如何延長作用域鏈?

1、執行環境的型別有什麼?
    a: 全域性變數
    b: 區域性變數(函式)

2、全域性執行環境是怎麼樣的?
    全域性執行環境是最外圍的也被認為是window物件的一個執行環境,ECMAscript實現所在的宿主環境不同,表示執行環境的物件也不一樣,所有全域性變數和函式都是作為window物件的屬性和方法建立的。每個執行環境都有一個與之關聯的變數物件,環境中定義的所有變數和函式都儲存在這個物件中。

3、全域性執行環境是在何時銷燬?
    某個執行環境中的所有程式碼執行完畢後,該環境被銷燬,儲存在其中的所有變數和函式定義也隨之銷燬。(全域性執行環境直到應用程式退出,例如關閉網頁或瀏覽器時才會被銷燬)。

4、程式中的執行流是由什麼機制控制的?
    每個函式都有自己的執行環境,當執行流進入一個函式時,函式的環境就會被推入一個環境棧中,而在函式執行之後,棧將其環境彈出,把控制權返回給之前的執行環境。ECMAscript程式中的執行流正是由這個方便的機制控制著。

5、什麼是作用域鏈?
    當代碼在一個環境中執行時,建立變數物件是一個作用域鏈。內部環境可以通過作用域鏈訪問所有的外部環境,但外部環境不能訪問內部環境中的任何變數和函式,這些環境之間的聯絡是線性,有次序的,每個環境都可以向上搜尋作用域鏈,以查詢變數和函式名,但任何環境都不能通過向下搜尋作用域鏈而進入另一個執行環境。

6、作用域鏈的用途是什麼?
    作用域鏈的用途,是保證對執行環境有權訪問的所有變數和函式的有序訪問,作用域鏈的前端,始終都是當前執行的程式碼所在環境的變數物件,如果這個環境是函式,則將其活動物件作為變數物件,活動物件在最開始時只包含一個變數,即arguments物件,作用域鏈中的下一個變數物件來自包含(外部)環境,而再下一個變數物件則來自下一個包含環境。這樣,一直延續到全域性執行環境,全域性執行環境的變數物件始終都是作用域鏈中的最後一個物件。7、識別符號是如何解析的?
    識別符號解析是沿著作用域鏈一級一級地搜尋識別符號的過程,搜尋過程始終從作用域鏈的前端開始,然後逐級地向後回溯,直至找到識別符號為止(如果找不到,則報錯)。
    如下:

    var color = "blue";

    function changeColor(){
        if(color === "blue"){
            color = "red";
        }else{
            color = "blue";
        }
    }

    changeColor();
    alert("color is now "+ color);

    以上函式changeColor()的作用域鏈包含兩個物件,它自己的變數物件(其中定義著arguments物件)和全域性環境的變數物件,可以在函式內部訪問變數color,就是因為可以在這個作用域鏈中找到它。此外,在區域性作用域中定義的變數可以在區域性環境中與全域性變數互相使用。
    如下:

    var color = "blue";

    function changeColor(){
        var anotherColor = "red";

        function swapColors(){
            var tempColor = anotherColor;
            anotherColor = color;
            color = tempColor;

            //這裡可以訪問color,anotherColor和tempColor
        }
        //這裡可以訪問 color 和 anotherColor ,但不能訪問 tempColor
        changeColor();
    }

    //這裡只能訪問 color
    changeColor();

    以上共涉及3個執行環境,全域性環境,changeColor() 的區域性環境和 swapColors的區域性環境,全域性環境中有一個變數 color 和一個函式 changeColor(), changeColor()的區域性環境中有一個名為 anotherColor 的變數和一個名為 swapColors() 的函式,但它也可以訪問全域性環境中的變數 color, swapColors 的區域性環境中有一個變數 tempColor,
    該變數只能在這個環境中訪問到。 
    無論是全域性環境還是changeColor() 的區域性環境都無權訪問 tempColor,然而,在swapColor()內部則可以訪問其他兩個環境中的所有變數,因為那兩個環境是其他的父執行環境;
    如下圖:

    對於 swapColor() 其作用域鏈中包含3個物件:swapColor() 的變數物件, changeColor() 的變數物件合全域性變數物件,swapColor() 的區域性環境開始時會先在自己的變數物件中搜索變數和函式名,如果搜尋不到則再搜尋上一級作用域鏈,
    changeColor() 的作用域鏈中只包含兩個物件,它自己的變數和全域性變數,因為它不能訪問 swapColor()的環境。8、如何延長作用域鏈?
    有些語句可以在作用域鏈的前端臨時增加一個變數物件,該變數物件會在執行程式碼後被移除,當執行流進入下列任何一個語句時,作用域鏈就會得到加長。
    try-catch 語句的 catch 塊:會建立一個新的變數物件,其中包含的是被丟擲的錯誤物件的宣告;
    with語句:會將指定的物件新增到作用域鏈中;
    如下:

    function buildUrl(){
        var qs = "?debug=true";

        with(location){
            var url = href + qs;
        }

        return url;
    }

    以上with語句接收的是location物件,因此其變數中就包含了location物件的所有屬性和方法,而這個變數物件被新增到了作用域鏈的前端。buildUrl()函式中定義了一個變數qs,當在with語句中引用變數href時(實際引用的 是location.href),可以在當前執行環境的變數物件中找到,當引用變數qs時,引用的則是在buildUrl()中定義的那個變數,而該變數位於函式環境的變數物件中,至於with語句內部,則定義了一個名為url的變數,因而url就成了函式執行環境的一部分,所以可以作為函式的值被返回。9、變數宣告會用在什麼環境下?
    使用var宣告的變數會自動被新增到最接近的環境中,在函式內部,最接近的環境就是函式的區域性環境,在with語句中,最接近的環境是函式環境,如果初始化變數時沒有使用var宣告,該變數會自動被新增到全域性環境。
    如下:

    function add(num1, num2){
        var sum = num1 + num2;
        return sum;
    }

    var result = add(10,20);
    alert(sum);    //由於sum不是有效的變數,因此會導致錯誤,區域性變數在全域性環境中不能訪問

    以上程式碼中的函式add()定義一個名為sum的區域性變數,該變數包含加法操作的結果,如果結果值從函式中返回了,但變數sum在函式外部是訪問不到的,如果省略這個例子中的var關鍵字,那麼當add()執行完畢後,sum也將可以訪問到:

    function add(num1, num2){
        sum = num1 + num2;
        return sum;
    }

    var result = add(10,20);
    alert(sum);    //30

    以上例子中的變數sum在被初始化賦值時沒有使用var關鍵字,於是,當呼叫完add()之後,新增到全域性環境中的變數sum將繼續存在;即使函式已經執行完畢,後面的程式碼依舊可以訪問它。10、作用域鏈的識別符號是如何查詢的?
    搜尋過程從作用域鏈的前端開始,向上逐級查詢與給定名字的匹配識別符號,如果在區域性環境中找到了該識別符號,搜尋過程停止,變數就緒,如果在區域性環境中沒有找到該變數名,則繼續沿著作用域鏈向上搜尋,搜尋過程將一直追溯到全域性環境中的變數物件,如果在全域性環境中也沒有找到,則意味著該變數尚未宣告。
    如下:

    var color = "blue";

    function getColor() {
        return color;
    }

    alert(getColor()); //blue

    以上呼叫getColor() 會引用變數color,為了確定變數color的值,將開始一個兩步的搜尋過程,首先,搜尋getColor()的變數物件,查詢其中是否包含一個名為color的識別符號,在沒有找到的情況下,搜尋繼續到下一個變數物件(全域性環境的變數物件),然後在那裡找到了名為color的識別符號,因為搜尋到了定義這個變數的變數物件,搜尋過程宣告結束。
    如下圖    

以上搜索過程中,如果存在一個區域性的變數的定義,則搜尋會自動停止,不再進入另一個變數物件,如果區域性環境中存在著同名識別符號,就不會使用位於環境中的識別符號;
    如下:

    var color = "blue";

    function getColor(){
        var color = "red";
        return color;
    }
    alert(getColor());    //red

    以上搜索過程首先從區域性環境中開始,在這裡發現了一個名為color的變數,其值為"red";變數找到了,所以搜尋停止。return語句就使用這個區域性變數,併為這個函式返回"red";任何位於區域性變數color的宣告之後的程式碼,如果不適用window.color都無法訪問全域性color變數。如果有一個運算元是物件,而另一個不是,就會在物件上呼叫valueOf()方法以取得基本型別的值,以便根據前面的規則進行比較。

提問式學習你將會更優秀!!GOGO加油!!

鼓勵自己養成良好的學習習慣,堅持不定時更