標籤:
1.閉包與變數
JavaScript中的範圍鏈的機制引出了一個副作用,即閉包只能取得包含函數中任何變數的最後一個值。閉包所儲存的是整個變數對象,而不是某個特殊的值。
| 1234567891011121314 |
function createFunctions(){ var result=new Array(); for (var i=0;i<10;i++){ result[i]=function(){ return i; }; } return result; } var funcs = createFunctions(); for (var i=0; i < funcs.length; i++){ document.write(funcs[i]() + "<br />"); } |
createFunction()函數返回一個數組。表面上看,似乎每個函數都應該返回自己的索引值,但事實並非如此,事實上每個函數的傳回值都是10.因為每個函數的範圍鏈中都包含著createFunctions()函數的使用中的物件,所以它們引用的都是同一個變數i。當createFunctions()函數返回後,變數i的值就是10,此時每個函數都引用著儲存變數i的同一個變數對象,所以每個函數返回後都是10.
當然我們可以使用匿名函數強制使閉包的行為符合預期。
| 12345678910111213141516 |
function createFunctions(){ var result=new Array(); for (var i=0;i<10;i++){ result[i]=function(num){ return function(){ return num; }; }(i); } return result; } var funcs = createFunctions(); for (var i=0; i < funcs.length; i++){ document.write(funcs[i]() + "<br />"); } |
在重寫了前面的createFunctions()函數後,每個函數就好返回各自不同的索引值了。在這裡,我們沒有直接把閉包賦值給數值,而是定義了一個匿名函數,並將立即執行該函數的結果賦值給數組。這裡的匿名函數有一個參數num,也就是最終的函數要返回的值。在調用每個匿名函數時,我們傳入了變數i。由於函數參數按值傳遞的,所以就會將變數i的當前值複製給參數num。而在這個匿名函數內部,有建立並返回了一個訪問num的閉包。這樣依賴,result數組中的每個函數都有自己num變數的一個副本,因此就可以返回各自不同的數值了。
1.2關於this對象
在閉包中使用this對象會出現一些問題,this對象是運行時基於函數的執行環境綁定的:在全域函數中,this等於window,而當函數被當作某個對象的方法調用時,this等於那個對象。不過,匿名函數的執行環境具有全域性,因此其this對象通常指向window(當然,在通過call()和apply()改變函數執行環境時,this指向其他對象)。
| 1234567891011 |
var name="The Window"; var object={ name:"My object", getNameFunc:function(){ return function(){ return this.name; }; } }; alert(object.getNameFunc()()); //"The Window"(在非strict 模式下) |
?
以上代碼建立了一個全域變數name,有建立了一個包含那麼屬性的對象,這個對象還包括一個方法——getNameFunc(),它返回一個匿名函數,而匿名函數又返回this.name.由於getNameFunc()返會一個函數。因此調用object.getNameFunc()()就會立即返回調用它的函數,結果就返回一個字串。然而,這個例子返回的字串是“The Window”,即全域name變數的值。
但是,為什麼匿名函數沒有取得其包含範圍(或外部範圍)的this對象呢?
每個函數在調用時,其使用中的物件都會自動擷取兩個特殊的變數:this和arguments。內建函式在搜尋這兩個變數時,只會搜到其使用中的物件為止,因此永遠不肯能訪問到外部函數中的這兩個變數。不過,把外部範圍中的this對象儲存在一個閉包能夠訪問的變數裡,就可以放閉包訪問該對象了。
| 123456789101112 |
var name="The Window"; var object={ name:"My object", getNameFunc:function(){ var that=this; return function(){ return that.name; }; } }; alert(object.getNameFunc()()); //"My object" |
以上代碼中,我們在定義匿名函數之前,把this對象賦值給了that變數,而在定義閉包之後,閉包也可以訪問這個變數,因為它們是我們在外部函數中特意聲明的一個變數。即使在函數返回之後,this也仍然引用的object,所以調用object.getName()()就返回“My object”.
Javascript 閉包與變數