JavaScript可以動態建立函式
並且在建立函式的同時還可以使用當下的所有上下文 (context) 資料
基於此原理,可曾想過以一個迴圈建立一套有規則的函式
當然,幾個月前也才吃過一點虧
所幸因緣際會可總算理解該怎麼辦
首先說說當時的吃到虧的寫法
假設要寫一個函式陣列 (大小 5),其中函式依序回傳 0 - 4值。
fnArray = []; for (var i = 0; i < 5; i++) fnArray[i] = function(){ return i; };
以上建立function後會發現
哇,怎麼每個函式執行回傳的數值都是一樣的 (4)
若要達成目的,必須換成下面這種寫法
fnArray = []; for (var i = 0; i < 5; i++) fnArray[i] = (function(x){ return function(){ return x; }; })(i);
看到這,很多人不是頭腦已經亂掉
不然就覺得這在寫什麼鬼,到底有啥不一樣
可執行看看吧,這會達到原先要達成的結果
因為沒看過詳細的語言定義
只能先提出這幾個感覺上的觀念
1. JavaScript當中,除了undefined 與 null外的所有東西都是物件
不管是函式、陣列、甚至小到一個數字或者是布林值,全部都是
只是表達形式與運算非常簡潔罷了
甚至讓你感覺不出「它」是一個物件
好比 1 與 1,雖然數值相同,可卻不是同一個物件
不過,==、!= 這幾個邏輯運算的可不會讓你感覺出來是不同的
總之,被隱藏起來的東西,愛怎麼說就怎麼說吧
只要能夠說得通,幫助思考便可
2. 一個物件必有其reference,任何的指派都是分享這個reference
3. 一個物件如果還有其他變數持有它的reference,則該物件是不會被消滅的
不管這個物件在哪個地方,哪個scope都是
4. 一個 function的執行,並不像傳統語言那樣
可能會重複利用著stack,畢竟它已經隔了一層引擎,自然可以不受那麼多束縛
可以說一個 function的執行,每執行一次所分配到的變數空間都是完全獨立的
5. 與各種程式語言相同,變數的scope也是外部可被內部共用,內部不可被外部使用
注意,這是指變數的那個名字可共用,而非其中持有的 reference
從上面提出的這幾點來看上述的發生問題的程式碼
雖然說建立了五個函式 ( function(){ return i; } )
這五個函式在當中,卻都指定要回傳 i 。
因為 i 這個變數乃由外部提供,亦即這五個函式,全部持有相同的 reference
也因此大家所回傳的數值都是一樣的
甚至當 i 的數值變化,回傳值亦會跟著變
欲實驗者,請將 var 拿掉讓 i 不受限於 for的 scope
可換到成功的範例中可不同了
首先看 ( function(x){...} )();
這就是 JavaScript的特色,可以動態產生函式
甚至可以立刻執行它
因為這是匿名的函式,執行完也沒人繼續儲存著
執行完成後立即被消滅
前面說過,每當執行一次就會產生一個獨立的變數空間
這五個空間內各包含一個 x 的變數
我們將其各自表示為 x-0、x-1、x-2、x-3、x-4
並透過匿名函式將 i 的 reference與其分享
即 x-0 持有 0、x-1 持有 1、x-2 持有 2、x-3 持有 3、x-4 持有 4
這個匿名的函式當中執行的內容為
回傳一個函式,並提供 x 變數供其 reference
因為函式 function (){ return x; } 被回傳出來並且儲存
這五個函式便不會消滅,五個函式不會被消滅
亦即代表著它們所持有的 x-0、x-1、x-2、x-3、x-4 的 reference也不會被消滅
由於分屬不同的 reference,自然陣列中每個函式的回傳值可達成所需要的效果
JavaScript的 Reference特性與 scope 可說是兩項非常大的利器
用得好,自然可以達到非常多簡潔便利的寫法
但是可得注意,頭腦要清楚
尤其得注意,不同的 scope當中有同名的變數與外部變數共享同時發生的時候
這得考驗程式人的辨別功力啦
留言列表