怎樣利用JavaScript中的閉包

JavaScript的變量作用域是通過函數來維護的。舉個例子,對於函數:

function add(a,b){ return a+b; }

而言,當使用不同的參數(不帶參數的函數同樣如此)調用它時:

var sum1 = add(1,2);
var sum2 = add(3,4);

每次調用都會通過創建一個新的調用對象維護一個新的函數作用域,從而保證了sum1和sum2分別取得相應的 值3和7。

而閉包的原理,也是如此。下面舉兩個例子,一個是因爲閉包導致了問題,而另一個則利用閉包巧妙地通過函數的作用域綁定參數。這兩個例子相關的HTML標記片斷如下:

<a href=”#” id=”closureExample”>利用閉包的例子(0.5秒後您會看到提示)</a>
<a href=”#” id=”closureExample2″>由於閉包導致問題的例子1</a>
<a href=”#” id=”closureExample3″>由於閉包導致問題的例子2</a>
<a href=”#” id=”closureExample4″>由於閉包導致問題的例子3</a>


例一:因遭遇閉包而導致問題

上面標記片斷中有4個<a>元素,假設要給後三個指定事件處理程序,使它們在用戶單擊時報告自己在頁面中的順序,比如:當用戶單擊第2個鏈接時,報告“您單擊的是第2個鏈接”。

爲此,如果編寫下列爲後三個鏈接添加事件處理程序的函數:

function badExample() { for (var i=2 ; i<=4 ; i++ ) { var el = document.getElementById(’closureExample’ + i); el.onclick = function(){ alert(’您單擊的是第’ + i + ‘個鏈接’); } } }

然後,在頁面載入完成後(不然可能會報錯)調用該函數:

window.onload = function(){ badExample(); }

此時單擊後3個鏈接,會看到警告框中顯示什麼信息呢?——全都是“您單擊的是第5個鏈接”。爲什麼?

因爲在badExample()函數中指定給el.onclick的事件處理程序——也就是那個匿名函數是在badExample()函數運行完成後(用戶單擊鏈接時)才被調用的。而調用時,需要對變量i求值,解析程序首先會在事件處理程序內部查找,但沒有定義。然後,又到外部作用域,即badExample()函數中查找,此時有定義,但i的值是5(只有i大於5纔會停止執行for循環)。因此,就會取得該值——這正是閉包(匿名函數)要使用其外部函數(badExample)作用域中變量的結果。而且,這也是由於匿名函數本身無法傳遞參數(故而無法維護自己的作用域)造成的。這個例子的問題怎麼解決呢?大家自己想一想。

例二:利用閉包綁定參數

還是上面的HTML片段,我們要在用戶單擊第一個鏈接時延時彈出一個警告框——注意延時——怎麼實現?答案是使用setTimeout()函數,這個函數會在指定的毫秒數之後調用一個函數,如:

setTimeout(someFunc,500);

但問題是,無法給其中的someFunc函數傳遞參數。而使用閉包則可以輕鬆解決這個問題:

function goodExample(i) { return function(){alert(i);}; }

函數goodExample用來返回一個匿名函數(閉包)。而我們可以通過爲它傳遞參數來使返回的匿名函數綁定該參數,如:

var good = goodExample(’這個參數是通過閉包綁定的’);

而此時,就可以將綁定了參數的good函數傳遞給setTimeout()實現延時警告了:

setTimeout(good,500) //此時good中已經綁定了參數

最後,與爲第一個鏈接指定事件處理程序結合起來,完整的代碼就是:

window.onload = function(){ var el = document.getElementById(’closureExample’); if (el){ var good = goodExample(’這個參數是由閉包綁定的’); el.onclick = function(){ setTimeout(good,500); } } }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章