對《javascript 雜談之哪種寫法你更喜歡?》最后一種“匿名函數(shù)法”的解釋
前兩天看了篇不錯(cuò)的關(guān)于javascript寫法的文章。在評論里,不少人表示看不懂最后一種方法,作者也沒詳細(xì)說明,于是我順勢解說,算是沾點(diǎn)人氣。

代碼如上圖,整體結(jié)構(gòu)模仿jQuery,代碼不長,知識(shí)點(diǎn)較多,信息量很大,我們分兩部分,逐行解釋。
第一部分
第一部分中,我們把extend相關(guān)的方法抽離,剩余代碼如下:
(function () { var yQuery = (function () { var yQuery = function () { return yQuery.fn.init(); }; yQuery.fn = yQuery.prototype = { init: function () { return this; } }; return yQuery; })(); window.yQuery = window.$ = yQuery(); })();
知識(shí)點(diǎn)1:無引用的匿名函數(shù)調(diào)用
(function() {
})();
這種寫法作用是聲明并執(zhí)行一個(gè)方法,等同于:
function Test() {
}
Test();
知識(shí)點(diǎn)2:屬性變量無需聲明
var obj = new Object(); obj.name = "abc";
obj并沒有name屬性,但無需聲明就可以使用,初始值為undefined。上例中yQuery.fn 就沒有聲明。
知識(shí)點(diǎn)3:{}二義性,相當(dāng)于創(chuàng)建一個(gè)對象。
var obj = new Object();
var obj = { };
在js中,{}除了可作為復(fù)合語句邊界以外,還有創(chuàng)建一個(gè)空對象的作用,因此上面這兩句相同。而在例子中用到的情況如下:
{
init: function () {
return this;
}
};
這段代碼猛一看像方法,實(shí)質(zhì)是一個(gè)包含了init方法的對象,而方法中的this指向這個(gè)對象本身。
知識(shí)點(diǎn)4:連等表達(dá)式
var a = b = 1;
這個(gè)比較好理解,相當(dāng)于對他們分別賦同一個(gè)值。在上例中,yQuery.fn就用到了這個(gè)寫法。
知識(shí)點(diǎn)5:原型繼承prototype
yQuery.fn = yQuery.prototype = {
init: function () {
return this;
}
};
例子中,通過連等分別對yQuery.fn和yQuery.prototype賦值給了同一個(gè)對象。而fn的作用只是一個(gè)別名,只為書寫方便,重點(diǎn)是給prototype賦值,為什么要給它賦值?在本例中無法解釋。
在jQuery中,init方法會(huì)返回不同對象,而本例中永遠(yuǎn)返回同一個(gè)對象,因此這里prototype沒有多大意義。至于jQuery為什么要用prototype,算是題外話了,有興趣的可點(diǎn)這里。
第一部分代碼含義
這段代碼第二行yQuery和第三行的yQuery是兩個(gè)變量,因名字相同,所有很有迷惑性。整段代碼意思就是:yQuery.prototype指向了一個(gè)包含init方法的對象,prototype有個(gè)別名fn,可通過yQuery.fn.init()返回這個(gè)對象,最后把這個(gè)對象賦值給window.$和window.yQuery屬性。
第二部分
看完了第一部分,第二部分就相對簡單了,代碼如下:
yQuery.extend = yQuery.fn.extend = function () { var options, name, src, copy, target = arguments[0] || {}, i = 1, length = arguments.length; if (length === i) { target = this; --i; } for (; i < length; i++) { if ((options = arguments[i]) != null) { for (name in options) { src = target[name]; copy = options[name]; if (src === copy) { continue; } if (copy!==undefined) { target[name] = copy; } } } } return target; };
知識(shí)點(diǎn)1:函數(shù)中的arguments變量
函數(shù)內(nèi)部會(huì)自帶一個(gè)arguments變量,該變量記錄傳入的參數(shù),從左至右分別是arguments[0],arguments[1]等,js奇怪的地方在于,你聲明了一個(gè)無參函數(shù),在調(diào)用的時(shí)候依然可以傳入?yún)?shù),比如:
function NoArg() { console.log(arguments[0]); console.log(arguments[1]); console.log(arguments[2]); } NoArg(1, 2, "a");
該例NoArg調(diào)用時(shí),會(huì)正常顯示傳入?yún)?shù)。
知識(shí)點(diǎn)2:結(jié)果類型不確定的邏輯運(yùn)算
js中,所有類型都可以進(jìn)行邏輯判斷(true或false),js會(huì)將值轉(zhuǎn)化為布爾值,但并不改變原值。如:
var str = "a"; if(str) { }
此處的str為true,該特性與邏輯運(yùn)算符”||”和”&&”結(jié)合形成了js一大特點(diǎn),代碼如下:
var str = "a"; var num = 1; var x = str || num; //x="a" var y = str && num; //y=1;
js支持“邏輯短路”,所謂邏輯短路是指:
- 在”||” 運(yùn)算中,第一個(gè)條件符合就結(jié)束判斷。
- 在”&&”運(yùn)算中,第一個(gè)條件不符合就結(jié)束判斷。
因此,”str || num”的str為true,則結(jié)束判斷,返回str。”str&&num”的str為true,則繼續(xù)判斷num,num為true,則返回num。在本文案例中,有幾個(gè)地方用到了這個(gè)特性:
target = arguments[0] || {}
容易看出,如果arguments[0]有值則返回該值,不然就通過{}返回一個(gè)空的對象。還有一處在圖片中有,我文中沒有打出來的代碼:
$.ui = $.ui || { };
知識(shí)點(diǎn)3:數(shù)組方式訪問對象屬性
var obj = new Object(); obj.name = "a"; obj["name"] = "a";
最后兩行代碼等效。
第二部分代碼含義
這部分代碼可簡單描述為:定義一個(gè)方法,將參數(shù)1之外的所有參數(shù)的屬性成員賦值給參數(shù)1。我把循環(huán)部分修改一下,能更容易看懂,代碼如下:
for (; i < length; i++) {
if ((options = arguments[i]) != null) {
for (var name in options) {
if (options[name] !== undefined) {
target[name] = options[name];
}
}
}
}
結(jié)語
js中有不少語法和運(yùn)算符存在二義性,這導(dǎo)致js代碼顯的混亂難懂,所以不少人罵js是一個(gè)2B的語言,我覺得也不無道理。不過隨著js的應(yīng)用范圍越來越廣泛,終究避不開它,因此罵歸罵,學(xué)還是要學(xué)的。
我是看了周愛民老師的《JavaScript語言精髓與編程實(shí)踐(第2版)》后才算對js真正入門,飲水思源,推薦此書。
聲明:個(gè)人博客已遷移到Github此處,新博客內(nèi)容大多與前端技術(shù)相關(guān)。

浙公網(wǎng)安備 33010602011771號