匿名函數自動調用的三種寫法如下:
var f1 = function(){alert("f1");}();(function(){alert("f2");}());void function(){alert("f3");}();
再來看一段代碼:
function Person(properties){for(var p in properties){(function(context){var t = p;context["get" + t] = function(){return properties[t];}context["set" + t] = function(val){properties[t] = val;}}(this));}}var p = new Person({name:"菩提樹下的楊過",sex:"男"});alert(p.getname());//彈出"菩提樹下的楊過"alert(p.getsex());//彈出"男"alert(p.name);//彈出:undefined
Person類為所有傳入的對象屬性,自動產生了getXXX與setXXX方法,這一段代碼雖然很短,卻包含了諸多js中的關鍵概念:
1.json對象標記法
當我們把"{name:"菩提樹下的楊過",sex:"男"}"做為參數,傳入Person建構函式時,實際上就建立了一個字典結構的索引值對:
name --> "菩提樹下的楊過"
sex --> "男"
即 name - value 結構,所以也就能用for ...in語句來遍曆了
以上結論,可以這樣測試
var obj = {name:"菩提樹下的楊過",sex:"男"};for(var p in obj){ alert("名稱:" + p + ",值:" + obj[p]);}
2.匿名函數的自動調用
這一段代碼結構可以簡化為:
function Person(properties){for(var p in properties){ ( function(){ ... }() ); }}
可以看到,裡面其實就是調用了匿名函數(即文章最開頭的第二種寫法)
3.函數調用時的上下文關係
每個函數調用時總會關聯一個上下文(如果找不到上下文,則最終會關聯到window對象)
function foo(fn){ //this.barbar = "Foo.barbar"; if (typeof fn === "function"){ fn(); } }var bar = { barbar : "Hello,World!", method:function(){ alert(this.barbar); }}bar.method(); //調用時,medhod中的this指的就是bar對象的上下文,此時this.barbar 與 bar.barbar等效 foo(bar.method);//調用時,這時bar.method中的this指代的是foo內部的上下文,而foo中並沒有barbar的定義,因此最終this.barbar其實就是foo.barbar,所以會彈出"undefined",如果把foo中的注釋行去掉注釋,就更能映證這一點
這是最近網上熱傳的"javascript令人費解的10件事"中的一段代碼,我在注釋中加了自己的理解,再回到文中的代碼,代碼的本意是想讓Person類動態添加對所有的屬性的getXXX與setXXX方法(通過匿名函數的自動調用),而匿名函數在執行時getXXX與setXXX函數的上下文this預設是指向匿名函數的,而非Person類本身!為瞭解決這個問題,不得不在匿名函數中增加了一個參數context,並且在調用時用(function(...){}(this));把Person的上下文this傳入到匿名函數中
4.閉包
關於閉包,不再做過多的學術解釋,先給一段代碼:
<ul> <li id="a1">aa</li> <li id="a2">aa</li> <li id="a3">aa</li></ul><script type="text/javascript">for (var i=1;i<= 3;i++){ var li = document.getElementById("a" + i); li.onclick = function(){ alert(i); }}</script>
解釋onclick產生了一個匿名函數,並引用外層的變數i,形成閉包,造成變數i在該函數中共用(可以理解為三個li的onclick函數中都引用同一個變數i),而i在迴圈結束後,變成4,因此所有li最終點擊都是彈出4
解決辦法:
<script type="text/javascript">for (var i=1;i<= 3;i++){ var li = document.getElementById("a" + i); li.i = i; li.onclick = function(){ alert(this.i); }}</script>
再回到文中的代碼,同樣匿名函數引用外層的變數p,形成閉包,如果不用var t = p;中轉一下變數,則最後所有的getXXX與setXXX方法,都是對應最後一個屬性的.