zoukankan      html  css  js  c++  java
  • 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         };

    这种写法,从功能上来说 没有什么问题, 但是在团队协作的时候, 会造成覆盖全局变量的问题, 那要大大降低覆盖的可能性, 一般会在外面套一个对象

     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

    也可以把原型对象上的所有方法,使用字面量方式简写

     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

    注意:  字面量方式和原型对象一个个添加   这两种不要混用, 否则会产生覆盖

    如果我们想把面向对象的使用方式更加的优雅,比如链式调用, 我们应该在每个方法中返回对象本身,才能继续调用方法, 即返回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上面添加

    我们继续把上面的方式,改成链式调用:  这里需要改两个地方, 一种是添加方法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();
  • 相关阅读:
    Docker私有仓库
    Docker入门
    HBase表的数据导出和导入
    HBase shell的使用记录
    Redis集群模式(Cluster)部署
    Oracle数据库sqlldr工具的使用
    oracle使用impdp和expdp导入导出数据
    Linux自动同步时间的方法
    Linux配置ssh免密码登陆
    Linux中逻辑卷(LV)的创建、增大和减小
  • 原文地址:https://www.cnblogs.com/ghostwu/p/7349233.html
Copyright © 2011-2022 走看看