资讯专栏INFORMATION COLUMN

javascript面向对象的常见写法与优缺点

Ajian / 3420人阅读

摘要:我们通过表单验证的功能,来逐步演进面向对象的方式对于刚刚接触的朋友来说,如果要写一个验证用户名,密码,邮箱的功能,一般可能会这么写表单验证全局全局全局这种写法,从功能上来说没有什么问题,但是在团队协作的时候,会造成覆盖全局变量的问题,那要大

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/87356.html

相关文章

  • 浅谈JavaScript面向对象

    摘要:不必在构造函数中定义对象实例的信息。其次,按照一切事物皆对象的这饿极本的面向对象的法则来说,类本身并不是一个对象,然而原型方式的构造函数和原型本身也是个对象。第二个问题就是在创建子类型的实例时,不能向超类型的构造函数中传递参数。 前言 对象(Object)应该算是js中最为重要的部分,也是js中非常难懂晦涩的一部分。更是面试以及框架设计中各出没。写这篇文章,主要参考与JavaScrip...

    cyixlq 评论0 收藏0
  • 复习Javascript专题(三):面向对象对象创建继承,原型及原型链)

    摘要:在创建子类实例时,不能向超类型的构造函数中传递参数。构造函数继承子类传进的值是基本思想是在子类构造函数的内部调用超类或父类型构造函数。继承保证构造函数指针指向如果想同时继承多个,还可使用添加属性的方式类继承, OOP:Object Oriented Programming 面向对象编程。 题外话:面向对象的范围实在太大,先把这些大的东西理解理解。 1.什么是对象? 根据高程和权威指南上...

    testHs 评论0 收藏0
  • 一名【合格】前端工程师自检清单

    摘要:在他的重学前端课程中提到到现在为止,前端工程师已经成为研发体系中的重要岗位之一。大部分前端工程师的知识,其实都是来自于实践和工作中零散的学习。一基础前端工程师吃饭的家伙,深度广度一样都不能差。 开篇 前端开发是一个非常特殊的行业,它的历史实际上不是很长,但是知识之繁杂,技术迭代速度之快是其他技术所不能比拟的。 winter在他的《重学前端》课程中提到: 到现在为止,前端工程师已经成为研...

    罗志环 评论0 收藏0
  • 一名【合格】前端工程师自检清单

    摘要:在他的重学前端课程中提到到现在为止,前端工程师已经成为研发体系中的重要岗位之一。大部分前端工程师的知识,其实都是来自于实践和工作中零散的学习。一基础前端工程师吃饭的家伙,深度广度一样都不能差。开篇 前端开发是一个非常特殊的行业,它的历史实际上不是很长,但是知识之繁杂,技术迭代速度之快是其他技术所不能比拟的。 winter在他的《重学前端》课程中提到: 到现在为止,前端工程师已经成为研发体系...

    isaced 评论0 收藏0
  • 【译】每个JavaScript 开发者应该了解10个面试题

    摘要:避免脆弱的基类问题。红牌警告没有提到上述任何问题。单向数据流意味着模型是单一的事实来源。单向数据流是确定性的,而双向绑定可能导致更难以遵循和理解的副作用。原文地址 1. 你能说出两种对 JavaScript 应用开发者而言的编程范式吗? 希望听到: 2. 什么是函数编程? 希望听到: 3. 类继承和原型继承的不同? 希望听到 4. 函数式编程和面向对象编程的优缺点? ...

    mykurisu 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<