用匿名函數解決一些scope問題
Thu, 13 Nov 2008 01:13:56 +0800這其實是剛剛在Crockford的書中瞄到的,但是很實用。
看看幾個例子吧。
常見到的問題之一,是想要在onclick事件處理函數中使用i變數,但是寫成這樣:
<html> <body> <input type="button" value="test1"> <input type="button" value="test2"> <input type="button" value="test3"> <script> var a = document.getElementsByTagName("input"); var i=0; for (i=0; i< a.length; i++) { a[i].onclick = function() { alert(i); } } </script> </body> </html>
結果在執行事件處理函數時,其實for迴圈已經跑完,所以永遠跑出2。這時候用一個匿名函數傳i進去,返回事件處理函數,就可以解決問題:
var a = document.getElementsByTagName("input"); var i=0; for (i=0; i< a.length; i++) { a[i].onclick = function(i) { return function(e) { alert(i); }; }(i); }
常見問題之二,是在函數物件實例中把事件處理函數指定給某個node:
function handle() { this.abc = "def"; this.setHandler = function(obj) { obj.onclick = function() { alert(this.abc); }; } } var a = new handle(); a.setHandler(document.getElementsByTagName('input')[0]);
這樣就會出現"undefined"訊息。
可以用匿名函數把this傳給事件處理函數,類似前面的解法:
function handle() { this.abc = "def"; this.setHandler = function(obj) { obj.onclick = function(that) { return function() { alert(that.abc); }; }(this); } } var a = new handle(); a.setHandler(document.getElementsByTagName('input')[0]);
最後一個常見問題是使用setTimeout,我們常常忘記這個函數是在global context下執行的。像這樣的例子就會出問題:
function handle() { this.abc = "def"; this.delayMsg = function(m) { setTimeout(function(){alert(this.abc);}, m); }; } var a = new handle(); a.delayMsg(600);
用匿名函數解決的方法還是類似:
function handle() { this.abc = "def"; this.delayMsg = function(m) { setTimeout(function(a){ return function(e){ alert(a.abc); }; }(this), m); }; } var a = new handle(); a.delayMsg(600);
所以用匿名函數,curry還有closure,也可以很直覺地解決scope問題呢。