執行環境:
// 定義了變量或函數有權訪問的其他數據,決定了它們各自的行為
// 每個執行環境都有一個變量對象與之對應,執行環境中所定義的所有變量和函數都保存在變量對象中
// 某個執行環境中的所有代碼執行完畢后,該執行環境被銷毀,保存在其中的所有變量和函數定義也隨之銷毀
函數與執行環境:
// 每個函數都有自己的執行環境.當執行流進入一個函數時,函數的環境就會被推入一個環境棧中.
// 在函數執行之后,棧將其環境彈出,把控制權返回給之前的執行環境.
作用域鏈:
// 當代碼在一個環境中執行時,會創建變量對象的一個作用域鏈.作用域鏈的用途,是保證對執行環境有權訪問的所有變量和函數的有序訪問。
// 作用域鏈的前端,始終都是當前執行的代碼(又稱為詞法作用域)所在環境的變量對象.如果這個環境是函數,則將其活動對象作為變量對象.
// 活動對象在最開始時只包含一個變量,即arguments對象.
// 作用域鏈中的下一個變量來自包含(外部)環境,而在下一個變量對象來自下一個包含環境.
// 這樣,一直延續到全局執行環境;全局執行環境的變量對象始終都是作用域鏈中的最后一個對象// 參考下面的例子
var color = "blue";
function changeColor() {var anotherColor = "red";function swapColors() {var tempColor = anotherColor;anotherColor = color;color = tempColor;}swapColors();
}
changeColor();
console.log(color);
// 作用域鏈的結構如下:
// Window
// -- color
// -- changeColor()
// -- anotherColor
// -- swapColors()
// -- tempColor// 一共3個執行環境:全局環境、changeColor()的局部環境和swapColors()的局部環境
// swapColors是changeColor的內部環境,changeColor是全局環境的內部環境
// 通過作用域鏈,可以從內而外 依次、有序的訪問.
下面從最簡單的全局作用域開始,深入挖掘各類解析方案從而涵蓋JavaScript提供的所有作用域
延長作用域鏈:
// 1.eval:可以接受一個字符串為參數,并將其中的內容視為好像在書寫時就存在于程序中這個位置的代碼
function foo(str, a) {eval(str);console.log(a, b);
}
var b = 2;
foo("var b = 3;",1 );// 等價于:
function foo(a) {var b = 3;console.log(a,b,);
}
var b = 2;
foo(1)
// 作用域鏈
// Window
// - b
// - foo
// - b
// - a// 2.with:通常被當作重復引用同一個對象中的多個屬性的快捷方式:
function foo(obj) {with(obj) {a = 2; }
};
var o1 = {a: 3
};
var o2 = {b: 3
};
foo(o1);
console.log(o1.a); // 2foo(o2);
console.log(o2.1); // undefined
console.log(a); // 2,泄露到全局上// o2的作用域,foo(...)的作用域和全局作用域中都沒有找到標識符a,因此在執行a = 2時,自動創建了一個全局變量
// ps:盡量不用eval 和 with,會影響執行效率!!!
先來看下面的一段代碼:
function foo() {var a = 2;function bar () {console.log( a );}return bar;
}
var baz = foo();
baz(); // 2// 1.函數bar()的詞法作用域可以訪問foo()的內部作用域,
// 2.在foo()執行后,由于javascript引擎的垃圾回收機制會釋放不在使用的內存空間.
// 3.閉包,阻止了foo()的內部作用于的釋放,原因在于bar()擁有涵蓋foo()內部作用域的閉包,使得foo的內部作用域一直存活,以供bar()一直使用// 注:bar()對foo作用域的引用,該引用叫做閉包(閉包可以使得函數繼續訪問定義時的詞法作用域).
參考 《JavaScript高級程序設計》(第3版)P73~P74
參考《你不知道的JavaScript》(上卷)P14~P57