咱们经过表单验证的功能,来逐步演进面向对象的方式. 对于刚刚接触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();