關於變量做用域的知識,相信學習JavaScript的朋友們必定早已經接觸過,這裏簡單列舉: javascript
- JavaScript中變量是以對象屬性的形式存在的:全局變量是全局對象的屬性;局部變量是聲明上下文對象的屬性。(聲明上下文對象是一個對用戶不可見的內部實現,沒法被引用,每當函數調用便建立這個對象以存放局部變量)
- JavaScript雖然是解釋型語言,但也存在預處理過程,其中便包含了聲明提早。JavaScript解釋器運行前,會先將整個程序中的變量聲明(包括函數)提早到做用域頂部執行。因此在程序中變量的使用語句能夠出如今聲明語句以前。
- JavaScript中沒有塊級做用域(像C語言那樣用方括號{}劃分做用域),而是使用函數做用域;
- 不在任何函數體內聲明的變量爲全局變量,擁有全局做用域,能夠在程序的任何位置被訪問;而在函數內聲明的變量(包括函數的參數)爲局部變量,擁有局部做用域,只在函數內部有定義;
- 若局部變量與全局變量重名,局部變量優先級高,能夠遮蓋全局變量;
- 局部做用域能夠相互嵌套(由於函數能夠嵌套)
而
做用域鏈的概念,接觸過的人就沒那麼多了。其實做用域鏈很好理解,上方第一條已經明確了,JavaScript中變量是以對象屬性的形式存在的,而做用域鏈其實就是這些對象組成的一個鏈表。全局變量是全局對象的屬性,局部變量是聲明上下文對象的屬性。這些對象按從內向外的順序連接,鏈尾固然就是全局對象。當函數運行中須要查找某一個變量的時候,就會沿着做用域鏈依次查詢每一個對象是否擁有與該變量同名的屬性,若是存在則直接使用;若整條鏈上都不存在這個屬性,便拋出ReferenceError。 在函數定義的時候,便建立了做用域鏈,鏈上的對象順序便肯定下來,當函數被調用,建立相應的對象添加到做用域鏈中,做用域鏈會隨着函數的定義調用而更新。在嵌套函數中更是如此,每次調用外部函數時,內部函數又會從新定義,隨之帶來的就是做用域鏈的相應變化。關於做用域鏈的數量,能夠用樹來比喻,全局對象是根節點,函數的嵌套表明着樹節點的層級關係,那麼一個程序中做用域鏈的數量等於對應樹中葉子節點的數量。
var a = 1;// 最外層的做用域鏈上只有全局對象
function func1(){
var a = 2; // 這一層的做用域鏈: func1的聲明上下文對象 ——> 全局對象
function func2(){
var a = 3; // 這一層的做用域鏈: func2的聲明上下文對象 ——> func1的聲明上下文對象 ——> 全局對象
console.log("in func2's scope a= " + a + " ");
}
func2();
console.log(console.log("in func1's scope a= " + a + " ");)
}
func1();
console.log(console.log("a= " + a + " "););
運行結果能夠很清楚的反映出做用域鏈的查詢順序。
關於做用域鏈還不得不提with語句,它能夠進行臨時性做用域鏈擴展。它的語法是這樣的:with(object){ statement }。 它將object添加到做用域鏈的頂部,而後執行語句,執行完畢後將做用域鏈恢復到原始狀態。在對象嵌套層次很深的時候,可使用with語句簡化代碼。下面的兩段代碼是等價的:
var object = {
object1: {
name : {
firstName: "hello",
lastName: "world",
nickName: "Jeff"
}
}
};
object.object1.name.firstName = "aaa";
object.object1.name.lastName = "bbb";
object.object1.name.nickName = "ccc";
var object = {
object1: {
name : {
firstName: "hello",
lastName: "world",
nickName: "Jeff"
}
}
};
with(object.object1.name){
firstName = "aaa";
lastName = "bbb";
nickName = "ccc";
}
with語句在嚴格模式中是被禁止使用的,在非嚴格模式也不推薦使用。使用with語句的代碼難以優化,而且運行速度更慢。因此上面這種狀況咱們可使用下面的方法等價使用,減小代碼:
var object = {
object1: {
name : {
firstName: "hello",
lastName: "world",
nickName: "Jeff"
}
}
};
var temp = object.object1.name;
temp.firstName = "aaa";
temp.lastName = "bbb";
temp.nickName = "ccc";