咱們經過表單驗證的功能,來逐步演進面向對象的方式. 對於剛剛接觸javascript的朋友來講,若是要寫一個驗證用戶名,密碼,郵箱的功能, 通常可能會這麼寫:javascript
1 //表單驗證 2 var checkUserName = function(){ 3 console.log( '全局checkUserName' ); 4 }; 5 var checkUserEmail = function(){ 6 console.log( '全局checkUserEmail' ); 7 }; 8 var checkUserPwd = function(){ 9 console.log( '全局checkUserPwd' ); 10 };
這種寫法,從功能上來講 沒有什麼問題, 可是在團隊協做的時候, 會形成覆蓋全局變量的問題, 那要大大下降覆蓋的可能性, 通常會在外面套一個對象java
1 var Utils = { 2 checkUserName : function(){ 3 console.log( 'Utils->checkUserName' ); 4 }, 5 checkUserEmail : function(){ 6 console.log( 'Utils->checkUserEmail' ); 7 }, 8 checkUserPwd : function(){ 9 console.log( 'Utils->checkUserPwd' ); 10 } 11 } 12 13 checkUserEmail(); 14 Utils.checkUserEmail();
上面這種方式,是字面量方式添加,在設計模式裏面,也稱爲單例(單體)模式, 與之相似的能夠經過在函數自己添加屬性和方法,變成靜態屬性和方法,達到相似的效果:設計模式
1 var Utils = function(){ 2 3 } 4 Utils.checkUserName = function(){ 5 console.log( 'Utils.checkUserName' ); 6 } 7 Utils.checkUserPwd = function(){ 8 console.log( 'Utils.checkUserPwd' ); 9 } 10 Utils.checkUserEmail = function(){ 11 console.log( 'Utils.checkUserEmail' ); 12 } 13 14 Utils.checkUserEmail(); 15 16 for( var key in Utils ){ 17 ( Utils.hasOwnProperty( key ) ) ? console.log( key ) : ''; 18 } 19 20 //加在函數上面的屬性和方法,沒法經過對象使用 21 var oUtil = new Utils(); 22 oUtil.checkUserEmail();//錯誤
還能夠經過函數調用方式,返回一個對象,把方法和屬性寫在對象中, 這種方式 跟面向對象沒有什麼關係,只是從函數的返回值角度來改造函數
1 //使用函數的方式 返回一個對象 2 var Util = function(){ 3 return { 4 checkUserName : function(){ 5 console.log( 'userName...' ); 6 }, 7 checkUserPwd : function(){ 8 console.log( 'userPwd...' ); 9 }, 10 checkUserEmail : function(){ 11 console.log( 'userEmail...' ); 12 } 13 } 14 } 15 Util().checkUserEmail();
還能夠經過相似傳統面嚮對象語言,使用構造函數方式 爲每一個實例添加方法和屬性, 這種方式,存在一個問題, 不能達到函數共用,每一個實例都會複製到方法.性能
1 var Util = function(){ 2 this.checkUserName = function(){ 3 console.log('userName'); 4 }; 5 this.checkUserEmail = function(){ 6 console.log('userEmail'); 7 }; 8 this.checkUserPwd = function(){ 9 console.log('userPwd'); 10 }; 11 } 12 13 var oUtil1 = new Util(); 14 var oUtil2 = new Util(); 15 console.log( oUtil1.checkUserEmail === oUtil2.checkUserEmail );//false 通常,咱們能夠經過原型屬性(prototype)改造這種方式,達到不一樣實例共用同一個方法 1 var Util = function(){ 2 3 }; 4 Util.prototype.checkUserName = function(){ 5 console.log('userName'); 6 }; 7 Util.prototype.checkUserPwd = function(){ 8 console.log('userPwd'); 9 }; 10 Util.prototype.checkUserEmail = function(){ 11 console.log('userEmail'); 12 }; 13 var oUtil1 = new Util(); 14 var oUtil2 = new Util(); 15 console.log( oUtil1.checkUserEmail === oUtil2.checkUserEmail );//true
也能夠把原型對象上的全部方法,使用字面量方式簡寫this
1 var Util = function(){ 2 3 }; 4 Util.prototype = { 5 checkUserEmail : function(){ 6 console.log( 'userEmail' ); 7 }, 8 checkUserName : function(){ 9 console.log( 'userName' ); 10 }, 11 checkUserPwd : function(){ 12 console.log( 'userPwd' ); 13 } 14 }; 15 var oUtil1 = new Util(); 16 var oUtil2 = new Util(); 17 console.log( oUtil1.checkUserEmail === oUtil2.checkUserEmail );//true
注意: 字面量方式和原型對象一個個添加 這兩種不要混用, 不然會產生覆蓋prototype
若是咱們想把面向對象的使用方式更加的優雅,好比鏈式調用, 咱們應該在每一個方法中返回對象自己,才能繼續調用方法, 即返回this設計
1 var Util = function(){ 2 return { 3 checkUserName : function(){ 4 console.log( 'userName...' ); 5 return this; 6 }, 7 checkUserPwd : function(){ 8 console.log( 'userPwd...' ); 9 return this; 10 }, 11 checkUserEmail : function(){ 12 console.log( 'userEmail...' ); 13 return this; 14 } 15 } 16 } 17 // 方法中若是沒有返回this,下面這種調用方式是錯誤的 18 Util().checkUserEmail().checkUserName(); 19 20 // 方法中返回對象自己,能夠鏈式調用 21 Util().checkUserEmail().checkUserName().checkUserPwd(); 1 var Util = function(){ 2 this.checkUserName = function(){ 3 console.log('userName'); 4 return this; 5 }; 6 this.checkUserEmail = function(){ 7 console.log('userEmail'); 8 return this; 9 }; 10 this.checkUserPwd = function(){ 11 console.log('userPwd'); 12 return this; 13 }; 14 } 15 16 new Util().checkUserEmail().checkUserName().checkUserPwd(); var Util = function(){ }; Util.prototype = { checkUserEmail : function(){ console.log( 'userEmail' ); return this; }, checkUserName : function(){ console.log( 'userName' ); return this; }, checkUserPwd : function(){ console.log( 'userPwd' ); return this; } }; new Util().checkUserEmail().checkUserName().checkUserPwd(); 1 var Util = function(){ 2 3 }; 4 Util.prototype.checkUserName = function(){ 5 console.log('userName'); 6 return this; 7 }; 8 Util.prototype.checkUserPwd = function(){ 9 console.log('userPwd'); 10 return this; 11 }; 12 Util.prototype.checkUserEmail = function(){ 13 console.log('userEmail'); 14 return this; 15 }; 16 17 new Util().checkUserEmail().checkUserName().checkUserPwd(); 在實際開發中,咱們常常須要擴展一些功能和模塊。擴展能夠在本對象或者父類對象或者原型上 1 Function.prototype.checkUserName = function(){ 2 console.log('ghostwu'); 3 }; 4 5 var fn1 = function(){}; 6 var fn2 = function(){}; 7 8 console.log( 'checkUserName' in fn1 ); //true 9 console.log( 'checkUserName' in fn2 ); //true 10 11 fn1.checkUserName(); //ghostwu 12 fn2.checkUserName(); //ghostwu 若是咱們使用上面這種方式擴展,從功能上來講,是沒有問題的,可是確形成了全局污染:通俗點說,並非說有的函數都須要checkUserName這個方法,而咱們這樣寫,全部的函數在建立過程當中都會從父類的原型鏈上繼承checkUserName, 可是這個方法,咱們根本不用, 因此浪費性能, 爲了解決這個問題,咱們應該要在須要使用這個方法的函數上添加,不是全部的都添加,另外關於in的用法,若是不熟悉,能夠看下個人這篇文章:當即表達式的多種寫法與注意點以及in操做符的做用 1 Function.prototype.addMethod = function( name, fn ){ 2 this[name] = fn; 3 } 4 5 var fn1 = function(){}; 6 var fn2 = function(){}; 7 8 fn1.addMethod( 'checkUserName', function(){console.log('ghostwu');}); 9 10 fn1.checkUserName(); //ghostwu 11 fn2.checkUserName(); //報錯
經過上述的改造,成功解決了全局污染, fn2這個函數上面沒有添加checkUserName這個方法,只在fn1上面添加code
咱們繼續把上面的方式,改爲鏈式調用: 這裏須要改兩個地方, 一種是添加方法addMethod能夠鏈式添加, 一種是添加完了以後,能夠鏈式調用對象
1 Function.prototype.addMethod = function( name, fn ){ 2 this[name] = fn; 3 return this; 4 }; 5 6 var fn1 = function(){}; 7 8 fn1.addMethod( 'checkUserName', function(){ 9 console.log( 'userName:ghostwu' ); 10 return this; 11 } ).addMethod( 'checkUserEmail', function(){ 12 console.log( 'userEmail' ); 13 return this; 14 } ).addMethod( 'checkUserPwd', function(){ 15 console.log( 'userUserPwd' ); 16 return this; 17 } ); 18 fn1.checkUserName().checkUserEmail().checkUserPwd(); 上面是屬於函數式 鏈式 調用, 咱們能夠改造addMethod方法, 在原型上添加函數,而不是實例上, 這樣咱們就能夠達到類式的鏈式調用 1 Function.prototype.addMethod = function( name, fn ){ 2 this.prototype[name] = fn; 3 return this; 4 }; 5 6 var fn1 = function(){}; 7 8 fn1.addMethod( 'checkUserName', function(){ 9 console.log( 'userName:ghostwu' ); 10 return this; 11 } ).addMethod( 'checkUserEmail', function(){ 12 console.log( 'userEmail' ); 13 return this; 14 } ).addMethod( 'checkUserPwd', function(){ 15 console.log( 'userUserPwd' ); 16 return this; 17 } ); 18 new fn1().checkUserName().checkUserEmail().checkUserPwd();