標籤:
閉包
// 建立一個建構函式quo// 它帶有get_status方法和status私人屬性var quo = function(status){ return { get_status: function(){ return status; } };};var myQuo = quo(‘amazing‘);alert(myQuo.get_status());
當我們調用quo時,它返回一個對象:
①該對象包含一個get_status方法,②該對象的一個引用儲存在myQuo中。
即使quo已經返回了(return status;),但get_status方法仍然有訪問status屬性的特權。
get_status方法訪問的並不是status參數的副本,而是status參數本身!
因為該函數可以訪問它被建立時所處的上下文環境。也因此該函數被稱為閉包。
再看一個更有用的栗子:
// 定義一個函數,它使一個DOM節點的背景色由黃變白var fade = function(node){ var level = 1; var step = function(){ var hex = level.toString(16); node.style.backgroundColor = ‘#FFFF‘ + hex + hex; if(hex < 15){ level += 1; setTimeout(step, 100); } }; setTimeout(step, 100);};fade(document.body);
只要fade的內建函式step需要level,level變數就會持續保留。
糟糕的栗子:
// 彈出節點的序號var add_the_handlers = function(nodes){ var i; for(i=0; i<nodes.length; i++){ nodes[i].onclick = function(){ alert(i); //總是彈出節點的數目 }; }};
未能達到目的的原因,是因為onclick時間處理器函數綁定了變數 i 本身,而不是函數在構造時的變數 i 的值。
(個人理解:js解析的時候,把for迴圈解析了一遍, 啟動並執行時候 i 已經等於節點數目了。綁定了 i 本身,故彈出的 i 等於節點數目。我們需要彈出的 i 是在它被構造時的值。)
解決糟糕的栗子:
var add_the_handlers = function(nodes){ var helper = function(i){ return function(){ alert(i); }; }; var i; for(i=0; i<nodes.length; i++){ nodes[i].onclick = helper(i); }};
避免在迴圈中建立函數,這會帶來無謂的計算,還會引起混淆。
可以在迴圈之外先建立一個輔助函數helper,讓輔助函數返回一個綁定了當前 i 值的函數,這樣就不會混淆了。
模組
我們可以使用函數和閉包來構建模組,這幾乎可以完全摒棄全域變數的使用。
模組是一個提供介面卻隱藏狀態和實現的函數或對象。
模組模式也可以用來產生安全的對象。
我們來構造一個產生序號的對象:
var serial_maker = function(){// 該對象包含一個設定首碼的方法// 該對象包含一個設定序號的方法// 和一個產生字串的方法 var prefix = ‘‘; //首碼 var seq = 0; //序號 return { set_prefix: function(p){ prefix = String(p); }, set_seq: function(s){ seq = s; }, generate: function(){ var result = prefix + seq; seq += 1; return result; } };};// 調用var seqer = serial_maker();seqer.set_prefix(‘Q‘);seqer.set_seq(1000);var unique = seqer.generate(); // "Q1000"
如果我們把seqer.generate作為一個值傳遞給第三方函數,這個函數能產生唯一的字串,但卻不能通過它來改變prefix和seq的值。
Javascript語言精粹(二)