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當中有同名的變數與外部變數共享同時發生的時候

這得考驗程式人的辨別功力啦

創作者介紹
創作者 wylokgo101 的頭像
wylokgo101

豆棚瓜架雨如絲 - WYLOKGO101

wylokgo101 發表在 痞客邦 留言(0) 人氣()