參考文獻: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 引擎在優化標識符查詢方面作得不錯,所以這個差異在未來恐怕就能夠忽略不計了。