javascript進階之路1
路漫漫其修遠兮,吾將禿了頭依然不見大明湖畔的夏雨荷。
當年我還是個javascript小白,項目經理分下來一個驗證表單功能的任務,內容不多,僅需要驗證用戶名、郵箱、密碼等。
我一看so easy,於是便寫下幾個函數。
1 function checkName () { 2 //驗證姓名 3 } 4 5 function checkEmail () { 6 //驗證郵箱 7 } 8 9 function checkPassword () { 10 //驗證密碼 11 }
正要將吾之傑作提交到團隊項目裏。
正在此時,一位頭發稀疏,腦殼發亮,滿臉胡渣的長者看著將要提交的代碼搖了搖頭說:“小白,等一下,先不要提交。”
“怎麽了?”
“你創建了很多全局變量呀。”
“全局變量?我只是寫了幾個函數而已。”
“函數不是變量麽?”長者反問道。
此時小白不知所措,心想:“難道函數是變量?”臉瞬間黑了下來。
長者見狀忙笑著說:“別著急,你看,如果我這麽聲明幾個變量來實現你的功能你看可以麽?”
1 var checkName = function () { 2 //驗證姓名 3 } 4 5 var checkEmail = function () { 6 //驗證郵箱 7 } 8 9 var checkPassword = function () { 10 //驗證密碼 11 }
“一樣的,只不過。。。。。。”
“對,只不過這個在用的時候要提前聲明,但是這麽看你就會發現你創建了3個函數保存在變量裏來實現你的功能,而你寫的是將你的變量名放在function後面而已,它也代表了你的變量。所以說你也聲明了3個全局變量。”
“這有什麽問題呢?”
“從功能上講當然沒問題,但是今天你加入了我們的團隊,在團隊開發中你所寫的代碼就不能只考慮自己了,也要考慮不影響到他人,如果別人也定義了同樣的方法就會覆蓋掉原有的功能了。如果你定義了很多方法,這種相互覆蓋的問題是很不容易察覺到的。”
“那我應該如何避免呢?”
“你可以將它們放在一個變量裏保存,這樣就可以減少覆蓋或被覆蓋的風險,當然一單被覆蓋,所有的功能都會失效,這種現象也是很明顯的,你自然也會很輕易地察覺到。”
“可是我該如何做呢?”小白迫不及待滴問道。
“一猜你就會問。”
“好吧,請你先簡單滴說一下。”
“對象你知道吧,它有屬性和方法,而如果我們要訪問它的屬性或方法時,可通過點語法向下遍歷查詢得到。我們可以創建一個檢測對象,然後把我們的方法放在裏面。”
1 var CheckObject = { 2 checkName : function () { 3 //驗證姓名 4 }, 5 checkEmail : function () { 6 //驗證郵箱 7 }, 8 checkPassword : function () { 9 //驗證密碼 10 }, 11 }
“此時我們將所有的函數作為CheckObject對象的方法,這樣我們只有一個對象,而我們要想使用它們也很簡單,比如檢測姓名CheckObject.checkName(),只是在我們原來使用的函數式前面多了一個對象名稱。”
“哦,這樣啊,但是我們既然可以通過點語法來使用方法,我們是不是也可以這麽創建呢?”
“當然,不過首先你要聲明一個對象,然後給它添加方法,當然在javascript中函數也是對象,所以你可以這麽做:”
1 var CheckObject = function () {}; 2 CheckObject.checkName = function () { 3 //驗證姓名 4 } 5 CheckObject.checkEmail = function () { 6 //驗證郵箱 7 } 8 CheckObject.checkPassword = function () { 9 //驗證密碼 10 }
“使用和前面一樣,比如CheckObject.checkName(),”長者接著說,“現在雖然能滿足你的需求,但當別人想用你的寫的對象方法時就有些麻煩了,因為這個對象不能復制一份,或者說這個對象類在用new關鍵字創建新的對象時,新創建的對象是不能繼承這些方法的。”
“但是復制又有什麽用呢?”小白不解地問道。
“給你舉個例子吧,假如你有一百塊,你的小夥伴看見了也想要一百塊,可只有一百塊怎麽辦?但如果你有一臺印鈔機,那麽好吧,誰想要就給他印一百。”
“哦,有些明白了,但是我該如何做呢?”
長者解釋道:“如果你想簡單地復制一下,你可以將這些方法放在一個函數對象中。”於是長者將代碼寫下。
1 var CheckObject = function () { 2 return { 3 checkName : function () { 4 //驗證姓名 5 }, 6 checkEmail : function () { 7 //驗證郵箱 8 }, 9 checkPassword : function () { 10 //驗證密碼 11 } 12 } 13 }
小白看了看代碼,思考一下說:“哦,你寫的看上去是,當每次調用這個函數的時候,把我們之前寫的那個對象返回出來,當別人每次調用這個函數時都返回了一個新對象,這樣執行過程中明面上是CheckObject對象,可實際上是返回的新對象。這樣每個人在使用時就互不影響了。比如想檢測郵箱可以像這樣吧。”
1 var a = CheckObject (); 2 a.checkEmail ();
“嗯,對”長者接著說,“雖然通過創建了新對象完成了我們的需求,但是他不是一個真正意義上類的創建方式,並且創建的對象a和對象CheckObject沒有任何關系(返回出來的對象本身就與CheckObject對象無關),所以我們還要對其稍加改造一下。”
1 var CheckObject = function () { 2 this.checkName = function () { 3 //驗證姓名 4 } 5 this.checkEmail = function () { 6 //驗證郵箱 7 } 8 this.checkPassword = function () { 9 //驗證密碼 10 } 11 }
“像上面這樣的對象就可以看成類了。”長者繼續說。
“那麽我們使用它還想之前那樣創建對象的方法創建麽?”小白追問道。
“不,既然是一個類,你就要用關鍵字new來創建了。”
1 var a = new CheckObject(); 2 a.checkEmail();
“這樣你就可以用CheckObject類創建出來的對象了。”
“如果我和我的小夥伴們都對類實例化了(用類創建對象),那麽我們每個人都會有一套屬於自己的方法吧。”小白不解地問道。
“當然,你看,我們是把所有的方法放在函數內部了,通過this定義的,所以每一次通過new關鍵字創建新對象的時候,新創建的對象都會對類的this上的屬性進行復制。所以這些新創建的對象都會有一套自己的方法,然而有時候這麽做造成的消耗是很奢侈的,我們需要處理一下。”
1 var CheckObject = function () { 2 3 }; 4 CheckObject.prototype.checkName = function () { 5 //驗證姓名 6 } 7 CheckObject.prototype.checkEmail = function () { 8 //驗證郵箱 9 } 10 CheckObject.prototype.checkPassword = function () { 11 //驗證密碼 12 }
“但有一點你要記住,這兩種方法不能混著用,否則一旦混用,如在後面為對象的原型對象賦值新對象時,那麽它將會覆蓋掉之前對prototype對象賦值的方法。”長者補充說。
“知道了,不過我們要使用這種方式定義的類是不是要像下面這樣呢?”小白問道。
1 var a = new CheckObject(); 2 a.checkName(); 3 a.checkEmail(); 4 a.checkPassword();
“沒錯,但是你發現沒,你調用了3個方法,但是你對對象a書寫了3遍。這是可以避免的,那就要在你聲明的每一個方法末尾處將當前對象返回,在javascript中this指向的就是當前對象,所以你可以將它返回。例如我們開始寫的第一個對象還記得麽?改動它很簡單,像下面這樣就可以。”
1 var CheckObject = { 2 checkName : function () { 3 //驗證姓名 4 return this; 5 }, 6 checkEmail : function () { 7 //驗證郵箱 8 return this; 9 }, 10 checkPassword : function () { 11 //驗證密碼 12 return this; 13 }, 14 }
“此時我們要想使用它就可以這樣:”
1 CheckObject.checkName().checkEmail().checkPassword();
“當然同樣的方式還可以放到類的原型對象中。”
1 var CheckObject = function () {}; 2 CheckObject.prototype = { 3 checkName : function () { 4 //驗證姓名 5 return this; 6 }, 7 checkEmail : function () { 8 //驗證郵箱 9 return this; 10 }, 11 checkPassword : function () { 12 //驗證密碼 13 return this; 14 } 15 }
“但使用時候也要先創建一下:”
1 var a = new CheckObject(); 2 a.checkName().checkEmail().checkPassword();
小白回顧著這些從未見過的代碼方式內心很激動,長者見小白對JavaScript如此著迷,於是補充了兩句。
“如果你看過prototype.js的代碼,我想你會想到下面的書寫方式。”
“prototype.js是什麽?”小白問道。
“一款javascript框架,裏面為我們方便地封裝了很多方法,它最大的特點就是對原生對象(JavaScript語言為我們提供的對象類,如Function、Array、Object等)的拓展,比如你想給每一個函數都添加一個檢測郵箱的方法就可以這麽做。”
1 Fucntion.prototype.checkEmail = function () { 2 //驗證郵箱 3 }
“這樣你在使用這個方法的時候就比較方便了,如果你習慣函數形式,那麽你可以這麽做。”
1 var f = new Function(); 2 f.checkEmail();
“但是你這麽做在我們這裏是不允許的,因為你汙染了原生對象Function,所以別人創建的函數也會被你創建的函數所汙染,造成不必要的開銷,但你可以抽象出一個統一添加方法的功能方法。”
1 Function.prototype.addMethod = function (name, fn) { 2 this[name] = fn; 3 }
“這樣如果你想添加郵箱驗證和姓名驗證方法你可以這樣做。”
1 var methods = function () {};
或者
1 var methods = new Function(); 2 methods.addMethod(‘checkName‘, function () { 3 //驗證姓名 4 }); 5 methods.addMethod(‘checkEmail‘, function () { 6 //驗證郵箱 7 }); 8 methods.checkName(); 9 methods.checkEmail();
“呀,這種方式很奇特呀。不過我想鏈式添加方法,是不是在addMethod中將this返回就可以呀,這麽做可以麽?”
1 Function.prototype.addMethod = function (name, fn) { 2 this[name] = fun; 3 return this; 4 }
“當然,所以你再想添加方法就可以這樣了:”
1 var methods = function () {}; 2 methods.addMethod(‘checkName‘, function () { 3 //驗證姓名 4 }).addMethod(‘checkEmail‘, function () { 5 //驗證郵箱 6 });
“那麽小白,我問你,我如果想鏈式使用你知道該如何做麽?”
小白想了想說,既然添加方法的時候可以將this返回實現,那麽添加的每個方法將this返回是不是就可以實現呢?”
於是小白這麽寫下:
1 var methods = function () { 2 }; 3 methods.addMethod(‘checkName‘, function () { 4 //驗證姓名 5 return this; 6 }).addMethod(‘checkEmail‘, function () { 7 //驗證郵箱 8 return this; 9 }); 10 11 methods.checkName().checkEmail();
“真的可以呀!”小白興奮滴說。
“可是在你測試的時候,你用的是函數式調用方式?對於習慣於類式調用方式的同學來說,他們可以這樣簡單更改一下。”
1 Function.prototype.addMethod = function (name, fn) { 2 this.prototype[name] = fn; 3 return this; 4 }
“此時我們還按照上一種方式添加方法。”
1 var Methods = function() {}; 2 Methods.addMethod(‘checkName‘, function () { 3 //驗證姓名 4 }).addMethod(‘checkEmail‘, function () { 5 //驗證郵箱 6 });
“但是我們在使用時就要註意了,不能直接使用,要通過new關鍵字來創建新對象了。”
1 var m = new Methods(); 2 m.checkEmail();
小白興奮滴看著這一行行代碼情不自禁地叫了一聲“這真是一種藝術”。
javascript進階之路1