JavaScript设计模式 Item 3 --封装

在JavaScript 中,并没有对抽象类和接口的支持。JavaScript 本身也是一门弱类型语言。在封装类型方面,JavaScript 没有能力,也没有必要做得更多。对于JavaScript 的设计模式实现来说,不区分类型是一种失色,也可以说是一种解脱。

从设计模式的角度出发,封装在更重要的层面体现为封装变化

通过封装变化的方式,把系统中稳定不变的部分和容易变化的部分隔离开来,在系统的演变过程中,我们只需要替换那些容易变化的部分,如果这些部分是已经封装好的,替换起来也相对容易。这可以最大程度地保证程序的稳定性和可扩展性。

javascript封装的的基本模式有3种。

1、使用约定优先的原则,将所有的私有变量以_开头

 <script type="text/javascript">/*** 使用约定优先的原则,把所有的私有变量都使用_开头*/var Person = function (no, name, age){this.setNo(no);this.setName(name);this.setAge(age);}Person.prototype = {constructor: Person,checkNo: function (no){if (!no.constructor == "string" || no.length != 4)throw new Error("学号必须为4位");},setNo: function (no){this.checkNo(no);this._no = no;}, getNo: function (){return this._no;setName: function (name){this._name = name;}, getName: function (){return this._name;},  setAge: function (age){this._age = age;},  getAge: function (){return this._age;},  toString: function (){return "no = " + this._no + " , name = " + this._name + " , age = " + this._age;}};var p1 = new Person("0001", "小平果", "22");console.log(p1.toString());        //no = 0001 , name = 小平果 , age = 22p1.setNo("0003");console.log(p1.toString());      //no = 0003 , name = 小平果 , age = 22p1.no = "0004";p1._no = "0004";console.log(p1.toString());    //no = 0004 , name =小平果 , age = 22</script>

看完代码,是不是有种被坑的感觉,仅仅把所有的变量以_开头,其实还是可以直接访问的,这能叫封装么,当然了,说了是约定优先嘛.

下划线的这种用法这一个众所周知的命名规范,它表明一个属性仅供对象内部使用,直接访问它或设置它可能会导致意想不到的后果。这有助于防止程序员对它的无意使用,却不能防止对它的有意使用。

这种方式还是不错的,最起码成员变量的getter,setter方法都是prototype中,并非存在对象中,总体来说还是个不错的选择。如果你觉得,这不行,必须严格实现封装,那么看第二种方式。

2、严格实现封装

<script type="text/javascript">/***  使用这种方式虽然可以严格实现封装,但是带来的问题是get和set方法都不能存储在prototype中,都是存储在对象中的* 这样无形中就增加了开销*/var Person = function (no, name, age){var _no , _name, _age ;var checkNo = function (no){if (!no.constructor == "string" || no.length != 4)throw new Error("学号必须为4位");};this.setNo = function (no){checkNo(no);_no = no;};this.getNo = function (){return _no;}this.setName = function (name){_name = name;}this.getName = function (){return _name;}this.setAge = function (age){_age = age;}this.getAge = function (){return _age;}this.setNo(no);this.setName(name);this.setAge(age);}Person.prototype = {constructor: Person,toString: function (){return "no = " + this.getNo() + " , name = " + this.getName() + " , age = " + this.getAge();}};var p1 = new Person("0001", "小平果", "22");console.log(p1.toString());        //no = 0001 , name =小平果 , age = 22p1.setNo("0003");console.log(p1.toString());      //no = 0003 , name = 小平果 , age = 22p1.no = "0004";console.log(p1.toString());    //no = 0003 , name = 小平果 , age = 22</script>

那么这与我们先前讲过的其他创建对象的模式有什么不同呢,在上面的例子中,我们在创建和引用对象的属性时总要使用this关键字。而在本例中,我们用var声明这些变量。这意味着它们只存在于Person构造器中。checkno函数也是用同样的方式声明的,因此成了一个私用方法。

需要访问这些变量和函数的方法只需要声明在Person中即可。这些方法被称为特权方法,因为它们是公用方法,但却能够访问私用属性和方法。为了在对象外部能访问这些特权函数,它们的前面被加上了关键字this。因为这些方法定义于Person构造器的作用域,所以它们能访问到私用属性。引用这些属性时并没有使用this关键字,因为它们不是公开的。所有取值器和赋值器方法都被改为不加this地直接引用这些属性。

任何不需要直接访问的私用属性的方法都可以像原来那样在Person.prototype中声明。像toString()方法。只有那些需要直接访问私用成员的方法才应该被设计为特权方法。但特权方法太多又会占用过多的内存,因为每个对象实例都包含所有特权方法的新副本。

看上面的代码,去掉了this.属性名,严格的实现了封装,只能通过getter,setter访问成员变量了,但是存在一个问题,所有的方法都存在对象中,增加了内存的开销。

3、以闭包的方式封装

<script type="text/javascript">var Person = (function (){//静态方法(共享方法)var checkNo = function (no){if (!no.constructor == "string" || no.length != 4)throw new Error("学号必须为4位");};//静态变量(共享变量)var times = 0;//return the constructor.return function (no, name, age){console.log(times++);    // 0 ,1 , 2var no , name , age;   //私有变量this.setNo = function (no)  //私有方法{checkNo(no);this._no = no;};this.getNo = function (){return this._no;}this.setName = function (name){this._name = name;}this.getName = function (){return this._name;}this.setAge = function (age){this._age = age;}this.getAge = function (){return this._age;}this.setNo(no);this.setName(name);this.setAge(age);}})();Person.prototype = {constructor: Person,toString: function (){return "no = " + this._no + " , name = " + this._name + " , age = " + this._age;}};var p1 = new Person("0001", "小平果", "22");var p2 = new Person("0002", "abc", "23");var p3 = new Person("0003", "aobama", "24");console.log(p1.toString());        //no = 0001 , name = 小平果 , age = 22console.log(p2.toString());      //no = 0002 , name = abc , age = 23console.log(p3.toString());    //no = 0003 , name = aobama , age = 24</script>

上述代码,js引擎加载完后,会直接执行Person = 立即执行函数,然后此函数返回了一个子函数,这个子函数才是new Person所调用的构造函数,又因为子函数中保持了对立即执行函数中checkNo(no) ,times的引用,(很明显的闭包)所以对于checkNo和times,是所有Person对象所共有的,创建3个对象后,times分别为0,1,2 。这种方式的好处是,可以使Person中需要复用的方法和属性做到私有且对象间共享。

这里的私用成员和特权成员仍然被声明在构造器。但那个构造器却从原来的普通函数变成了一个内嵌函数,并且被作为包含它的函数的返回值给变量Person。这就创建了一个闭包,你可以把静态的私用成员声明在里面。位于外层函数声明之后的一对空括号很重要,其作用是代码一载入就立即执行这个函数。这个函数的返回值是另一个函数,它被赋给Person变量,Person因此成了一个构造函数。在实例华Person时,所调用的这个内层函数。外层那个函数只是用于创建一个可以用来存储静态成员的闭包。

在本例中,checkno被设计成为静态方法,原因是为Person的每个实例都生成这个方法的一个新副本毫无道理。此外还有一个静态属性times,其作用在于跟踪Person构造器的总调用次数。

版权声明:本文为小平果原创文章,转载请注明:http://blog.csdn.net/i10630226

转载于:https://www.cnblogs.com/dingxiaoyue/p/4948174.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/375428.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【WCF安全】WCF 自定义授权[用户名+密码+x509证书]

1.x509证书制作(略) 2.直接贴代码 ----------------------------------------------------------------------服务端------------------------------------------------------------------------------------------- WCF服务 1 using System;2 using System.Collections.Generi…

openMVS-编译

opencv4 编译 会有问题&#xff0c;可以重新下载 opencv3 编译并指定好路径。 OpenCV_DIRyour opencv3 build install path cmake -DCMAKE_BUILD_TYPERelease -DVCG_ROOT"$main_path/vcglib" ..

ASP.NET Web API 数据提供系统相关类型及其关系

转载于:https://www.cnblogs.com/frankyou/p/4932651.html

openMVG跑自定义数据出错

使用自己拍摄的图片跑 openMVG 的 turtor_demo.py 时&#xff0c;出现错误&#xff0c;没有生成 sfm_data.bin DSC01988" model "DSC-RX100M6" doesnt exist in the database Please consider add your camera model and sensor width in the database.原因时数…

windows server 2003下安装iis6+php

参照http://www.myhack58.com/Article/sort099/sort0100/2012/35579.htm 这篇文章&#xff0c;即可&#xff01; 前 面我写了《windows安装PHP5.4Apache2.4Mysql5.5》的安装教程&#xff0c;本地实现是很简单的&#xff0c;但是有人还是喜欢用IIS来配置 PHP环境&#xff0c;部分…

将 JAR 转为 EXE – JSMOOTH 的使用教程(第二期)(转载)

http://www.iteknical.com/convert-jar-to-exe-phase-ii-jsmooth-use-tutorial/转载于:https://www.cnblogs.com/leinuo2016/p/4932790.html

“”要求左值

错误 C2102 “&”要求左值 wrong code typedef struct CodeData {void *ptr_;CodeData(void*ptr) : ptr_(ptr){} } CodeData;typedef struct Data {int data_;data(int data) : data_(data){} } Data;// 这里出错&#xff0c;因为&后面是临时变量&#xff0c;不能取地…

winform自定义文件程序-- 不允许所请求的注册表访问权(ZSSQL)

常见问题1&#xff1a; 不允许所请求的注册表访问权 win7、win8 双击程序文件ZSSQL时候会出现 不允许所请求的注册表访问权 的弹窗异常 解决方法&#xff1a;ZSSQL.exe 右键 属性--兼容性--以管理员身份运行此程序 转载于:https://www.cnblogs.com/DemoLee/p/4173324.html

UITabBarController使用总结

刚看了几天教程就开始跟着开发了&#xff0c;以前也没学过C&#xff0c;太痛苦了~只能看看大神的博客&#xff0c;自己再总结学习一下了。 1.首先新建一个TabBarViewController继承于UITabBarController。然后什么都不用写&#xff0c;相当于装各个tab页的容器。 2.给每个视图都…

Auto-Configuration Error: Cannot find gcc or CC

bazel 编译的时候出错 首先 echo $CC 检查&#xff0c;若输出无值&#xff0c;则 export CCcc

Effective Modern C++英文版及中文翻译

https://pan.baidu.com/s/1uqEBGHn3dcVON18oRK5LNQ 提取码&#xff1a;gqqv 中文版不用看了&#xff0c;译者估计自己都不怎么用c11\14&#xff0c;翻译的巨垃圾。

第一个 mac 程序 Create-JSON-Model

第一个 mac 程序 Create-JSON-Model 效果图 数据 {"ID":null,"name":"Doe","first-name":"John","age":25,"hobbies":["reading","cinema",{"sports":["volley-bal…

php中utf8 与utf-8

php中utf8 与utf-8 原文:php中utf8 与utf-8相信很多程序员刚开始也会有这样的疑惑&#xff0c;如题&#xff0c;我也是。 其实&#xff0c;他们可以这样来区分。 一、在php和html中设置编码&#xff0c;请尽量统一写成“UTF-8”,这才是标准写法&#xff0c;而utf-8只是在…

编译vtk

https://vtk.org/Wiki/VTK/Configure_and_Build#On_Windows

Android--简单开发和使用ContentProvider数据共享

今天学习的时候学到了ContentProvider数据共享这个东东&#xff0c;所以自己写了个小例子: 我们要开发ContentProvider的话&#xff0c;需要创建一个类去继承ContentProvider,里面会让你重写四个方法&#xff0c;这四个方法就是数据共享用到的方法 包括SQLite的插入、查询、删除…

ECharts数据图表系统? 5分钟上手!

目录&#xff1a; 前言简介方法一&#xff1a;模块化单文件引入(推荐)方法二&#xff1a;标签式单文件引入【前言】 最近在捣鼓各种插件各种框架&#xff0c;发现这个ECharts还是比较不错的&#xff0c;文档也挺全的&#xff0c;还是中文的&#xff0c;给大家推荐一下。 这篇文…

vscode 配置 pcl头文件库

ctrl shift p 输入Edit configuretion 在includePath种添加 “${PCL_ROOT}/include/pcl-1.12/”

Python正则表达式中的re.S

Python正则表达式中的re.S title: Python正则表达式中的re.S date: 2014-12-21 09:55:54 categories: [Python] tags: [正则表达式,python] --- 在Python的正则表达式中&#xff0c;有一个参数为re.S。它表示“.”&#xff08;不包含外侧双引号&#xff0c;下同&#xff09;的作…

MySQL数据库安全配置

文章来源&#xff1a;http://www.xfocus.net MySQL数据库安全配置1、前言MySQL 是完全网络化的跨平台关系型数据库系统&#xff0c;同时是具有客户机/服务器体系结构的分布式数据库管理系统。它具有功能强、使用简便、管理方便、运行速度快、安全可靠性强等优点&#xff0c;用户…

slidingmenu属性

转载原文 http://www.cnblogs.com/xueqiang911226/p/3564757.html 最近用到slidingmenu&#xff0c;要了解这个库&#xff0c;首先得了解属性和方法&#xff0c;特意记录以备以后方便查询。 SlidingMenu 常用属性介绍: menu.setMode(SlidingMenu.LEFT);//设置左滑菜单 slidin…