前言
咳咳,葉大俠病了,昨天晚上回家時候在車上就不舒服,果然回來就掛了,本來還想今天接著上班撐下去的。但是昨天又看到一個IT巨子掛了,所以果斷請了一個假!!!
但是早上7.00左右就迷迷糊糊的醒了,於是我在想我是不是該“身殘志堅”一番。。。。
咳咳,以上玩笑,我們最近一起學習了很多CSS的東西,相信大家的CSS水平必定提高了吧???所以我們接下來一段時間來看看javascript吧,今天我們一起來看看閉包這個傢伙!
本文參考:
http://www.cnblogs.com/TomXu/
http://www.cnblogs.com/lhb25/archive/2011/09/06/javascript-scope-chain.html
http://www.cnblogs.com/frankfang/archive/2011/08/03/2125663.html
http://www.cnblogs.com/xiaotie/archive/2011/08/03/2126145.html
執行內容(execution context)
又是上下文。。。無論是曾經學習.net時候的httpcontext,還是最近學習的block formatting context,都是上下文!
我們可以感覺到內容相關的存在,就如感覺得到空氣一樣,我們卻將它拿不出來,於是我們來看看執行內容吧。
每次當控制器轉到ecmascript可執行代碼的時候,就會進入一個執行內容。活動的執行內容組在邏輯上為一個堆棧,堆棧最底部一定是全域上下文(global context),頂部便是當前上下文
這個句話是很有玄機的哦,比如我們在一個函數環境中,我們首先用的是當前變數,若是沒有就在父級函數中找,最後就在window中找。
PS:我們在寫部落格時候,中文沒有問題,突然打一個字母上,你會發現整行都下移了1/2px,各位知道神馬原因嗎???:)
eval在執行時候會產生一個調用內容相關的東東(calling context),所以eval中定義的變數會影響函數環境中的變數。
範圍
閉包有範圍鏈有很多說不清道不明的關係,所以我們還是先看看範圍吧。
任何語言都會有範圍的概念,範圍便是變數與函數可存取範圍,簡稱變數與函數的生命週期。javascript中分為全域與局部兩種,直白點window與function
var name = '葉小釵';function func() { var id = '刀狂劍癡'; function alertId() { alert(id); } alertId();}alert(name);//葉小釵alert(id);//錯誤func(); //刀狂劍癡alertId();//錯誤
此處name屬於window屬性便是全域的(有些瀏覽器內建name屬性小心陷阱!)
func也是全域的,但是func這個範圍鏈包含了全域的,所以他可以訪問外部的,但是外部不能訪問進來。
鏈子。。。
在js中什麼都是對象,函數和其它對象一樣擁有很多屬性,其中一個比較特殊的屬性是:
[[scope]],scope翻譯過來就是範圍,該屬性包含了函數被建立的範圍中的對象的集合:
於是傳說中的範圍鏈誕生了,他決定了哪些資料能被哪些函數訪問。
當一個函數建立後,他的範圍鏈會被建立此函數的範圍中可訪問的資料對象填充:
function add (a, b) { var sum = a + b; return sum;}
在函數add建立時,他的範圍鏈中會填入一個全域對象,我們可以想象為window被壓入了其[[scope]]
函數add的範圍在調用時會被用到,此時會建立一個“運行時上下文(execution context)”的內部對象,他定義了函數執行時的環境,每個運行時上下文都有自己的範圍鏈,用於標識符解析,當execution context建立時,而他的範圍鏈初始化為當前運行函數的[[scope]]所包含的對象。
所有相關的東東會按照其在函數中出現的順序被複製到運行時內容相關的範圍鏈中,他們共同組成了“使用中的物件(activation object)”,該對象就包含了函數的所有局部變數,參數,以及this,然後會排在我們範圍堆棧的頂端,我們可以最先擷取,在運行時上下文結束(函數執行完畢)後,GC便會回收其空間。
在函數執行過程中,遇到一個變數便會經曆一次標識符解析以決定從哪裡擷取以及儲存資料。
這個過程首先從我們的“堆棧”頂端開始搜尋,也就是使用中的物件的開始,若是有該變數便擷取值,
若是沒有則向下,知道window為止,沒有就報錯!
記憶體溢出
是想我們的瀏覽器記憶體若是搞滿了,自然會瀏覽器崩潰,這是我們閉包之中需要考慮到的東西,因為他會導致我們一些東西無法GC。
閉包,神奇的魔法
閉包是神奇的魔法,因為他幹了很多“壞事”,達到了出人意料的結果。
所謂閉包,便是function嵌套function,內部function可以訪問外部的變數。若是外部函數返回了內建函式,那麼就閉了一個包
1 function a() { 2 var name = '葉小釵'; 3 function alertName() { 4 alert(name); 5 } 6 return alertName; 7 } 8 9 var func = a();10 func();//葉小釵
這個傢伙是個壞孩子,怎麼說呢,壞孩子招人疼啊!
我們看第9行,按我們之前的理解a執行結束後,整個使用中的物件是不是該被銷毀?是不是與a有關的東西都該GC呢?
確實是這樣的,但是我們這裡就產生了一個閉包,阻止了其銷毀,因為他裡面的變數被用到了。
這裡的一個事實便是外部的func訪問到了a內部的變數!
一個經典的例子:
1 function outer() { 2 var o = {}; 3 for (var i = 0; i < 10; i++) { 4 o[i] = function () { 5 alert(i); 6 } 7 } 8 return o; 9 }10 var funcs = outer();11 for (var k in funcs) {12 funcs[k]();13 }14 var s = '';
我們來看這個,大家應該都比較熟悉了,我們知道他全部會列印10。
這是因為我們將outer給予funcs時候,outer的任務就完成啦,他現在內部的i便是10,我們現在再來調用函數,所有的i自然是指向一個[[scope]],所以變數i就是10!!!
要怎麼解決大家也一目瞭然:
1 function outer() { 2 var o = {}; 3 for (var i = 0; i < 10; i++) { 4 o[i] = (function (i) { 5 alert(i); 6 })(i) 7 } 8 return o; 9 }10 var funcs = outer();11 for (var k in funcs) {12 funcs[k]();13 }14 var s = '';
相當於每次i是以一個副本的方式重新複製了一番,這個是可以達到我們的目的,但是有時候會對效能有一點影響,我們還是需要注意。
我們這裡就不扯遠了,在扯遠的話我估計也露餡了。。。呵呵,最後我們來說一點應用吧。
閉包的應用
jquery時如何神奇的使用閉包的我現在還看不透,那是我1年後的任務,我這不關注他。
我這裡說的應用時原來項目過程中遇到的,所以沒有代碼,我這邊說下問題吧:
我們點擊一個按鈕,會向伺服器發一個ajax請求,其中包含一些data,然後資料返回回來後我們又會用到開始傳過去的一些data中的敏感資訊,比如id。
這個時候一種比較笨的方法就是伺服器給我們傳回來,但是閉包出現後,我們便可以直接在函數中定義函數,而使用我們之前的data即可。
該例子有段時間了,大家看不清楚也沒有關係啦。。。。
結語
葉大俠病了,求安慰,求按頂。。。。。