【JS. ES5重點筆記】執行環境和做用域

【JavaScript.ES5】執行環境和做用域

參考文獻:javascript

  • Nicholas C.Zakas 《JavaScript》高級程序設計前端

僅爲我的學習參考文獻的內容記錄的筆記。存在部分直接拿來的成分。不得不說,這本書寫得很透徹。java

引言和基本概念

執行環境是JS中很重要的概念,其定義了變量或者函數訪問其餘數據的權限,決定了其各自的行爲。函數

每一個函數都有本身的執行環境。當執行流進入一個函數時,函數的環境就會被推入一個環境棧中。而在函數執行以後,棧將其環境彈出,把控制權返回給以前的執行環境。 ECMAScript 程序中的執行流正是由這個方便的機制控制着。學習

當代碼在一個環境中執行時,會建立變量對象的一個做用域鏈(scope chain)。做用域鏈的用途,是保證對執行環境有權訪問的全部變量和函數的有序訪問。做用域鏈的前端,始終都是當前執行的代碼所在環境的變量對象。若是這個環境是函數,則將其活動對象(activation object)做爲變量對象。活動對象在最開始時只包含一個變量,即 arguments 對象(這個對象在全局環境中是不存在的)。做用域鏈中的下一個變量對象來自包含(外部)環境,而再下一個變量對象則來自下一個包含環境。這樣,一直延續到全局執行環境;全局執行環境的變量對象始終都是做用域鏈中的最後一個對象。優化

標識符解是沿着做用域鏈一級一級往下拓展的,而搜素則是從做用域鏈的前端開始逐級回溯,直到找到標識符爲止(找不到的話固然就報錯了)。ui

下面請看簡單的事例:url

var color = "blue";

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

changeColor();

alert("Color is now " + color);

經過運行能夠發現,存行結果是紅色,運行changecolor時,會去搜索color的值,在函數體內,未發現color的定義,因而會往外找,結果找到了color="blue"的定義,因而就會訪問,獲取,修改。debug

var color = "blue";

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

    function swapColors(){
        var tempColor = anotherColor;
        anotherColor = color;
        color = tempColor;
        
        //color, anotherColor, and tempColor are all accessible here
    }

    //color and anotherColor are accessible here, but not tempColor        
    swapColors();
}

changeColor();

//anotherColor and tempColor aren't accessible here, but color is
alert("Color is now " + color);

上面涉及了3個執行環境,全局、changecolor()和swapColors三種。其中全局變量是color和anotherColor。changeColor()的局部環境中有一個名爲anotherColor 的變量和一個名爲swapColors()的函數,但它也能夠訪問全局環境中的變量 color。 swapColors()的局部環境中有一個變量tempColor,該變量只能在這個環境中訪問到。不管全局環境仍是 changeColor()的局部環境都無權訪問 tempColor。然而,在 swapColors()內部則能夠訪問其餘兩個環境中的全部變量,由於那兩個環境是它的父執行環境。設計

延長做用域鏈

下列兩種狀況能夠延長做用域鏈:

  • try-catch語句的catch語句塊

  • with語句

這兩個語句都會在做用域鏈的前端添加一個變量對象。對 with 語句來講,會將指定的對象添加到做用域鏈中。對 catch 語句來講,會建立一個新的變量對象,其中包含的是被拋出的錯誤對象的聲明。

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

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

    return url;
}

var result = buildUrl();
alert(result);

with接受了location對象,因而變量對象就是location的全部屬性和方法,變量對象就被添加在做用域鏈前端。buildUrl()函數中有局部變量qs。with引用變量href時,即location.href,可以被找到。引用變量qs,指的則是buildUrl()函數內部的qs。至於 with 語句內部,則定義了一個名爲 url 的變量,於是 url 就成了函數執行環境的一部分,因此能夠做爲函數的值被返回。

沒有塊級的做用域

JS中並無塊級做用域。在類C語言中是有的,如條件語句,循環語句之類,大括號內屬於一種做用域,可是在JS中並無。

if (true) {
var color = "blue";
}

alert(color); //"blue"

顯然,color在條件語句的大括號中,var定義的也是局部變量,但實際上,大括號外面能訪問到想訪問的color。

聲明變量

使用 var 聲明的變量會自動被添加到最接近的環境中。並且僅限於該環境。反之,若是該聲明沒有帶,var會被自動認爲是全局變量。

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

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

這裏報錯了,由於sum在add函數中爲局部變量,從大括號外面並不能訪問到sum。若是改爲下面這樣,就能正常運行。

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

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

查詢標識符

當在某個環境中爲了讀取或寫入而引用一個標識符時,必須經過搜索來肯定該標識符實際表明什麼。搜索過程從做用域鏈的前端開始,向上逐級查詢與給定名字匹配的標識符。若是在局部環境中找到了該標識符,搜索過程中止,變量就緒。若是在局部環境中沒有找到該變量名,則繼續沿做用域鏈向上搜索。搜索過程將一直追溯到全局環境的變量對象。若是在全局環境中也沒有找到這個標識符,則意味着該變量還沒有聲明,會報錯。

var color = "blue";

function getColor(){
    return color;
}

alert(getColor()); //"blue"

alert中調用了getcolor()函數,這個函數中訪問了color,然而函數中並無聲明color,因而會向上找color,在全局中找到了,因而就取值。

var color = "blue";

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

alert(getColor()); //"red"
alert(color);//"blue"

getcolor函數體內有color的聲明,並且是局部的,因此並不會覆蓋全局的blue。在getcolor內部,color就是內部聲明的color,一旦搜索到定義以後就不會再搜索,因此就是red;而外面的color則並不會由於裏面的color是red而改變,因此仍是blue。

補充: 變量查詢也不是沒有代價的。很明顯,訪問局部變量要比訪問全局變量更快,由於不用向上搜索做用域鏈。 JavaScript 引擎在優化標識符查詢方面作得不錯,所以這個差異在未來恐怕就能夠忽略不計了。

相關文章
相關標籤/搜索