**執行環境(executin context)**是JS中最為重要的一個概念。執行環境定義了變量或函數有權訪問的其他數據決定了它們各自的行為。每個執行環境都有一個與之關聯的變量對象(variable object),環境中定義的所有變量和函數都保存在這個對象中。雖然我們編寫的代碼無法訪問這個對象
,但解析器在處理數據時會在后臺使用它。
全局執行環境是最外圍的一個執行環境。根據JS實現的宿主環境不同,表示執行環境的對象也不一樣。在Web瀏覽器中,全局執行環境被認為是Window對象,因此所有全局變量和函數都是作為window對象的屬性和方法創建的。某個執行環境中的所有代碼執行完畢后,改環境被銷毀,保存在其中的所有變量和函數定義也隨之銷毀(全局執行環境直到應用程序退出-例如關閉網頁或瀏覽器時才會被銷毀);
每個函數都有自己的執行環境。當執行流進入一個函數時,函數的環境就會被推入一個環境棧中。而在函數執行之后,棧將其環境彈出,把控制權返回給之前的執行環境。JS程序中的執行流正是由這個方便的機制控制著。
當代碼在一個環境中執行時,會創建變量對象的一個作用鏈域(scope chain)。作用域鏈的用途,是保證對執行環境有權訪問的所有變量和函數的有序訪問。作用域鏈的前端,始終都是當前執行的代碼所在環境的變量對象。如果這個環境是函數,則將其活動對象(activation object)作為變量對象。活動對象在最開始時只包含一個變量,即arguments對象(這個對象在全局環境變量中是不存在的)。作用域鏈中的下一個變量對象來自包含(外部)環境,而再下一個變量對象則來自下一個包含環境。這樣,一直延續到全局執行環境;全局執行環境的變量對象始終都是作用域中的最后一個對象。
標識符解析是沿著作用域一級一級地搜索標識符的過程。搜索過程始終從作用域鏈的前端開始,然后逐級的向后回溯,直至找到標識符為止(如果找不到標識符,通常會導致錯誤發生)。
~~~
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 color1="red";
function swapColors(){
var color2=color1;
color1=color;
//這里可以訪問所有的變量
}
//這里只能訪問color1和color;
}
//這里只能訪問color;
changeColor();
alert("Color is now "+color);
~~~
以上代碼共涉及三個執行環境:全局環境,changeColor()的局部變量和swapColors()的局部變量環境。方法函數里面可以訪問全局變量,但是無法訪問比這個方法函數范圍更小的局部變量,因為那兩個環境都是它的父執行環境。
內部環境可以通過作用域鏈訪問所有的外部環境,但外部環境不能訪問內部環境中的任何變量和函數。這些環境之間的聯系是線性,有次序的。每個環境都可以向上搜索作用域鏈,以查詢變量和函數名;但任何環境不能通過向下搜索作用域鏈而進入另一個執行環境。在上面的這個例子中swapColors里包含三個對象:他會先從自己的變量對象中搜索變量和函數名,如果搜索不到則再搜索上一級作用域鏈。
**延長作用域鏈:**
雖然執行環境的類型只有兩種-全局和局部。但還是有其他辦法來延長作用域鏈。就是在有些語句可以再作用域鏈的前端臨時增加一個變量對象,改變量對象會在代碼執行后被移除。再兩種情況下會發生這種現像。具體來說,就是當執行流進入下列任何語句的時候,作用域就會得到加長:
try-catch語句的catch塊;
with語句;
這兩個語句都會在作用域鏈的前端添加一個變量對象。對with語句來說,會將指定的對象添加到作用域鏈中。對catch語句來說,會創建一個新的變量對象,其中包含的是被拋出的錯誤對象的聲明。
~~~
var add = function(a,b){
if(typeof a!="number"||typeof b!="number"){
throw{
name:"類型錯誤",
message:"參數需要number類型"
}
}
}
try{
add(1,"fg");
}catch(e){
alert(e.name);
}
finally{
alert("finally");
}
~~~
當我們執行到if這段代碼的時候會創建異常類,我們在throw里面會指定異常的名稱和異常的提示。再catch里面會創建一個新的變量對象,但是我們在這個對象里面,可以得到這個所拋出的異常,這就是創建一個新的變量對象,并且它的作用域延長了。
沒有塊級作用域:
JS沒有塊級作用域經常會導致理解上的困惑。在我比
較熟悉的Java語言里面,由花括號封閉的代碼都有自己的作用域。因而支持根據條件來定義變量。
~~~
if(true){
var color="blue";
}
alert(color)
~~~
執行完這段代碼,發現可以打印為blue;在Java中,color變量會在if語句執行完畢后被銷毀。而在我們的JS中,if語句和for循環語句會被添加到當前的執行環境也就是全局變量中;
~~~
for(var i=0;i<=10;i++){
}
alert(i); //11
~~~
當執行完循環之后,i的值為11,有人說這里為什么不是10啦。我也很奇怪,在早起的JS里面這個i打印出來應該是10的,后來為了區分開來,在for循環結束之后又累加了一次。
**聲明變量:**
使用var聲明的變量會自動被添加到最近的環境中,也就是我們所謂的函數局部環境,在with語句中,最接近的環境是函數環境。如果初始化時變量沒有使用var聲明,該變量為自動被添加到全局環境。
~~~
function add(num1,num2){
sum=num1+num2;
return sum;
}
var result=add(1,2);
alert(sum); //3
~~~
**查詢標示符:**
這個很好理解,當我們在某個環境中需要讀取而引用一個標識符代表某種特定含義的時候,必須通過搜索來確定該標識符。搜索才有由近到遠,由局部到全局,由小到大,如果查詢到了相應的標示符將停止搜索;
~~~
var color="blue" ; //標示為黃色
function getColor(){
var color="red"; //標識為紅色
return color;
}
alert(getColor()); //red
~~~
- 前言
- javascript基礎與定義
- JavaScript變量
- JavaScript變量二(數據類型,Number,String,Object)
- JavaScript傳參的問題
- JavaScript基本類型和引用類型的值
- JavaScript執行環境及作用域
- JavaScript垃圾收集
- JavaScript和JQuery和angularjs操作select
- 搞對象前,你得先有對象
- JavaScript數組
- JavaScript運行原理解析
- Jquery和angularjs獲取check框選中的值小技巧
- JavaScript函數表達式
- JavaScript的閉包理解
- JavaScript BOM
- JavaScritpt的DOM初探之Node(一)
- 為什么說DOM操作很慢
- jQuery性能優化