面試的時候一直會被問到什麼是閉包,之前也不是很在乎,更沒有去總結和概括.javascript
閉包就是可以讀取其餘函數內部變量的函數。
因此,在本質上,閉包就是將函數內部和函數外部鏈接起來的一座橋樑。java
(一)閉包最基本的應用:面試
少廢話,上代碼 仍是<<javascript高級程序設計>>的栗子,
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 compare = createComparisonFunction("name"); var result = compare({ name: "Nicholas" }, { name: "Greg" });
分析:
(1)閉包函數能夠訪問其外部函數
return出來的這個匿名函數就是一個閉包函數,這個匿名函數中的訪問了外部函數的活動對象,就是這個propertyName參數。由於外部的做用域鏈被這個匿名函數所包含(也能夠理解爲:compare函數包含
createComparisonFunction()函數的活動對象和全局變量對象),因此返回的這個匿名函數能夠一直訪問他外部的這個propertyName以及全局變量。
(2)閉包所引用的外部變量不會由於所在做用域銷燬而銷燬
由於在返回的閉包函數中,引用了外部函數的活動對象,因此createComparisonFunction()內的活動對象(即propertyName)在createComparisonFunction()執行完後不會被銷燬。由於雖然createComparisonFunction執行完後,會把其執行環境中的做用域鏈銷燬,可是他的活動對象仍然被閉包函數引用,放入了匿名函數的執行環境的做用域中數組
(二)閉包的反作用閉包
(1).閉包只能取得包含函數中任何變量的最後一個值函數
function createFunctions(){ var result = new Array(); for (var i=0; i < 10; i++){ result[i] = function(){ return i; }; } return result; } var res = createFunctions(); console.log(res[0]()); //10 console.log(res[1]()); //10
原理:
由於每一個函數的做用域鏈中都保存着 createFunctions() 函數的活動對象,因此它們引用的都是同一個變量 i 。 當createFunctions()函數返回後,變量 i 的值是 10,此時每一個函數都引用着保存變量 i 的同一個變量對象,因此在每一個函數內部 i 的值都是 10this
解決方法:設計
獲取內部函數的對象result[i]時,使用匿名函數,並在匿名函數中再使用閉包函數,使得當前環境下的num被閉包函數函數調用,存儲在做用域中不會被釋放.
function createFunctions2(){ var result = new Array(); for(var i = 0 ; i <10 ; i++){ result[i] = (function(num){ return function(){ return num } })(i) } return result; } var res2 = createFunctions2(); console.log(res2[0]()); // 0 console.log(res2[1]()); // 1
原理:
定義了一個匿名函數,並將當即執行該匿名函數的結果賦給數組。這裏的匿名函數有一個參數 num,也就是最終的函數要返回的值。在調用每一個匿名函數時,咱們傳入了變量 i。因爲函數參數是按值傳遞的,因此就會將變量 i 的當前值複製給參數 num。而在這個匿名函數內部,又建立並返回了一個訪問 num 的閉包。這樣一來,result 數組中的每一個函數都有本身num 變量的一個副本,所以就能夠返回各自不一樣的數值了。code
(2).當閉包函數外部包含了一個匿名函數,this指向全局對象
var name = "The Window"; var object = { name: "My Object", getNameFunc: function () { return function () { //匿名函數執行具備全局性 return this.name; //this指向window }; } }; console.log(object.getNameFunc()()) // The Window
原理:
每一個函數在被調用時都會自動取得兩個特殊變量:this 和 arguments。內部函
數在搜索這兩個變量時,只會搜索到其活動對象爲止,因此沒法獲取到外部的this,此時getNameFunc()返回的是一個匿名函數,而且匿名函數具備全局性,所以this指向全局的window
解決方案:
把外部做用域中的 this 對象保存在一個閉包可以訪問到的變量裏,就可讓閉包訪問該對象了
var name2 = "The Window"; var object2 = { name:"My Object", getNameFunc:function(){ var that = this; //將外部函數的this保存在外部函數的活動對象中(函數中申明的變量中) return function (){ return that.name } } } console.log(object2.getNameFunc()()) //My Object
(三)閉包的缺點(1).因爲閉包會攜帶包含它的函數的做用域,所以會比其餘函數佔用更多的內存。過分使用閉包可能會致使內存佔用過多(2).閉包只能取得包含函數中任何變量的最後一個值,因此要注意寫法