注:這個系列是本人對js知識的一些梳理,其中很多內容來自書籍:Javascript高級程序設計第三版和JavaScript權威指南第六版,感謝它們的做者和譯者。有發現什麼問題的,歡迎留言指出。前端
var color = "blue"; function changeColor() { if(color == "blue"){ color = "red"; }else{ color = "blue"; } } changeColor(); console.log("color is:" + color);//red
例子中,函數changeColor()的做用域鏈包含兩個對象:它本身的變量對象(其中定義着arguments對象)和全局環境的變量對象。能夠在函數內部訪問變量 color,就是由於能夠在做用域鏈中找到它。數組
var color = "blue"; function changeColor() { var anotherColor = "red"; function swapColors() { var tempColor = anotherColor; anotherColor = color; color = tempColor; //這裏能夠訪問color、anotherColor和tempColor } //這裏能夠訪問color和anotherColor,但不能訪問tempColor swapColors(); } // 這裏只能訪問color changeColor();
以上共涉及3個執行環境:全局環境、changeColor()的局部環境和swapColors()的局部環境。顯然,內部環境能夠經過做用域鏈訪問全部的外部環境,但外部環境不能訪問內部環境中的任何變量和函數。瀏覽器
閉包是指有權訪問另外一個函數做用域中的變量的函數。建立閉包的常見方式,就是在一個函數內部建立另外一個函數。閉包
function createComparisonFunction(propertyName) { return function (object1, object2) { var value1 = object1[propertyName]; var value2 = object2[propertyName]; if(value1<value2){ return -1; }else if(value1>value2){ return 1; }else{ return 0; } } } //建立函數 var compareNames = createComparisonFunction("name"); //調用函數 var result = compareNames({name:'jaychou'},{name:'xiaoming'}); //解除對匿名函數的引用(釋放內存) compareNames = null;
以上過程:函數
1.定義函數內部的函數時,會將它的包含函數的活動對象添加到它的做用域鏈中。在此例中,在匿名函數被返回後,它的做用域鏈初始化爲包含createComparisonFunction()函數的活動對象和全局變量對象。設計
2.createComparisonFunction()函數在執行完畢後,其活動對象也不會被銷燬,由於匿名函數的做用域鏈仍然在引用這個活動對象,結果就是隻是createComparisonFunction()的執行環境的做用域鏈會被銷燬,其活動對象會留在內存中。code
3.直到代碼中將匿名函數置爲null釋放內存後,createComparisonFunction()的活動對象纔會被銷燬。對象
注意:因爲閉包會攜帶包含它的函數的做用域,因此會比其餘函數佔用更多的內存,過多使用閉包會致使內存佔用過多,因此在頗有必要時才考慮使用閉包。ip
function createFunctions() { var result = new Array(); for(var i=0;i<10;i++){ result[i] = function () { return i; }; } return result; } console.log(createFunctions()[4]());//會打印10
數組裏面的每一個函數都是打印10,由於每一個函數的做用域鏈中都保存着createFunctions()函數的活動對象,因此他們引用的都是同一個變量i。當函數數組被返回時,變量i的值是10,因此就是上面的結果了。經過建立另外一個匿名函數的改造以下符合預期:內存
function createFunctions() { var result = new Array(); for(var i=0;i<10;i++){ result[i] = function (num) { return function () { return num; } }(i); } return result; } console.log(createFunctions()[4]());//會打印4
沒有直接把閉包賦值給數組,而是定義了一個匿名函數,並將當即執行該匿名函數的結果賦給函數,函數參數是按值傳遞的,因此會把變量i的當前值複製給參數num。
在這個當即執行函數裏面建立並返回了一個訪問num的閉包。這個閉包被返回時都準確包含了對應的包含環境的活動對象的num值。
等等
整體而言,閉包對於咱們理解執行環境,理解做用域鏈頗有幫助,但日常若是不是頗有必要就不要用,佔用內存比較多。