javascript面向对象三大特征之封装实例详解


Posted in Javascript onJuly 24, 2019

本文实例讲述了javascript面向对象三大特征之封装。分享给大家供大家参考,具体如下:

封装

封装(Encapsulation):就是把对象内部数据和操作细节进行隐藏。很多面向对象语言都支持封装特性,提供关键字如private来隐藏某些属性和方法。要想访问被封装对象中的数据,只能使用对象专门提供的对外接口,这个接口一般为方法。调用该方法能够获取对象内部数据。

在JavaScript语言中没有提供专门的信息封装关键字,不过可以使用闭包来创建,只允许从对象内部访问的方法和属性。另外,接口也是数据封装的一种工具,接口提供了外界访问方法的约定。在应用开发中,所有类都应定义接口,类只向外提供已实现接口中规定的方法,任何别的方法都是隐藏的。其所有属性都是私有的,外界只能通过接口中定义的存取操作与之打交道。

---引自 《jQuery开发从入门到精通》,不过原书有错误,或者可能是我错买了盗版,不过下面代码都是经过我修改的,没有问题。

被动封装

被动封装:就是对对象内部数据进行适当约定,这种约定具有很强的主观性,没有强制性保证,主要针对公共对象而言。一般来说,JavaScript所包含的数据都是公开的,没有隐私可言,其中的信息可以随意被访问。下面给出一个例子来阐述:

var Person = function (name,gender) {
 if(name === undefined) {
 throw new Error("name is necessary")
 } else {
 this.name = name;
 }
 if(gender === undefined) {
 throw new Error("gender is necessary")
 } else {
 this.gender = gender;
 }
}
var p = new Person("Tom",1); // 如果我们实例化的时候,如果传递参数错误了,将会报异常

为了数据安全,代码中适当的增加了一些条件限制,避免非法信息侵入。

var Animal = function (species) {
 if(!this.checkSpecies(species)) {
 throw new Error('species is illegal');
 } else {
 this.species = species;
 }
}
Animal.prototype = {
 checkSpecies:function(species){
 // 检测 species , 参数为 species , 合法返回true ,这里可自定义检测逻辑
 // 这里先让它直接返回 true
 return true;
 }
}
var ani = new Animal("dog");

从更安全和扩展的角度来说,凡是类都应该定义接口,这样能够保证数据存取更加安全,同时也方便团队的合作。内部私有方法检测和接口措施可在一定程度上保护对象内部数据,但是他们也存在一个致命漏洞(这些属性和方法可以被公开重置,面对公开覆盖属性和方法值,任何人都无法阻止这种行为。),比如这样的:

var Util = function() {};
Util.prototype = {
 _say:function(){
 console.log("这里有一个私有的成员 _say方法");
 }
}
// 下面我们来修改它
Util.prototype._say = function(){
 console.log('哈哈,我已经被修改了');
}
// 开始检测
var util = new Util;
util._say(); // 哈哈,我已经被修改了

同时内部检测和接口可以在一定程度上占用了系统开销,这个问题也是必须认真考虑的。

就像上面的代码那样,一般我们约定了区别公共和私有成员,就是在一些方法和属性名称前或者后加下划线,来表示私有。

下划线命名是一种约定俗称的命名规范,表示一个属性和方法仅供内部使用,直接访问可能会导致意外发生。

以上数据保护方式都是被动性防御,因为他们只是一种约定,只有在遵守的时候才有效果。主要适用于非敏感性的内部方法和属性。

主动封装

在js中,因为函数有作用域,在函数内部声明的变量,在函数外部无法访问。而所以的私有和公有的区别就在于在对象外部是否可以访问。所以实现封装的最佳选择是使用函数的作用域。举例:

function haha() {
 var n = 1;
 function hehe() {
 n++;
 }
 hehe();
 return n;
}
console.log(haha()); // 2

函数haha内部有私有变量n,在此作用域中,hehe能够访问n,而haha外部的任何函数都无法访问haha里的n。

同样的,我们在函数内部返回hehe,就可以在外部来调用haha私有函数hehe。

function haha() {
 var n = 1;
 function hehe() {
 return ++n;
 }
 return hehe;
}
console.log(haha()()); // 2

函数作用域内部的方法无法被外界访问,但是在函数作用域内部的其他公共方法可以访问到它们,于是利用公共方法为中转站,可以巧妙的把内部私有方法公开化。因此这些公共方法也被称为特权方法,即在方法前面加this关键字。

使用这种方式创建的对象具有真正的封装性如:

function A() {
 // 私有方法 _xx
 function _xx(){}
 // 公有方法
 this.xx = function() {
 return _xx;
 }
}
A.prototype = {
 other:function() {
 console.log("other方法代表不需要访问私有属性和方法的方法,一般放在原型里");
 }
}

但是这种方式也有一些缺点:

① 新生成的每个实例对象都会拷贝构造函数中的属性和方法,而私有的_xx 无疑在每次实例化的时候都会重复拷贝,这样会占用大量内存,所以不适合大量使用,仅在必要时适当使用

② 不利于类的继承,所有派生的子类都不能访问超类中的私有属性和方法。不过可以使用特权方法来访问超类中的私有属性和方法。举个例子:

// B 是超类
function B() {
 var _private = 1;
 function _checkPrivate() {
 return _private;
 }
 this.checkPrivate = function() {
 return _checkPrivate;
 }
}
// C 是派生类
function C() {
 B.call(this); // 用call 实现继承 ,后面我们会讲到如何继承
 // this.private = _private; // 这里尝试访问超类的私有属性,因为无法访问,会报错
}
// 实例化 派生类C
var c = new C();
// console.log(c.private); // 和在C内部的尝试一样,是无法访问的,报错
// 不过可以用下面的方法访问超类中的私有属性
console.log(c.checkPrivate()()); // 1

静态方法

在面向对象编程中,大多数的方法和属性与类的实例产生联系。还有一种情况是,静态属性和方法是与类本身直接联系,可直接从类访问,也就是说,静态成员在类上操作,而不是实例上。在JavaScript中的Math和Global都是静态对象,不需要实例化就可直接访问。

类的静态成员,包括私有和公共两种。他们在系统中只有一份副本,意思就是说他们不会被分成多份传递给不同的实例对象。而是通过函数指针进行引用。书上有个例子非常好,下面举例:

var F = (function(){ // 把闭包函数赋给F,返回一个构造函数
 var _a = 1; // 定义一个闭包体内的私有变量
 this.a = _a; // 闭包体内的公共属性 a
 this.get1 = function(){ // 闭包体内的公共方法get1
 return _a;
 }
 this.set1 = function(x){ // 闭包体内的公共方法set1
 _a = x;
 };
 return function(){ // 返回一个构造函数,构造函数也是函数,更是对象(相当于一个类)
 this.get2 = function() { // 类的get2方法
  return _a;
 };
 this.set2 = function(x) { // 类的set2方法
  _a = x;
 };
 }
})();
// 定义类的静态公共方法和属性
F.get3 = function(){ // 定义一个静态方法get3
 return get1(); // 这里可以直接使用 F内的公共方法get1
}
F.set3 = function(x) { // 定义一个静态方法set3
 return set1(x); // 同get1
}
// 下面开始测试
var f = new F(); // 实例化这个类
console.log(f.get2()); // 1 用实例对象访问公共方法get2
F.set3(3); // 调用静态方法set3
console.log(F.get3()); // 3
F.A = ++a; // 定义一个静态属性A
console.log(F.A); // 2

我们推荐使用这种闭包的封装方式

感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.3water.com/code/HtmlJsRun测试上述代码运行效果。

更多关于JavaScript相关内容可查看本站专题:《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
IE innerHTML,outerHTML所引起的问题
Jun 04 Javascript
jQuery实现HTML5 placeholder效果实例
Dec 09 Javascript
jQuery过滤选择器详解
Jan 13 Javascript
AngularJS手动表单验证
Feb 01 Javascript
javascript实现一个简单的弹出窗
Feb 22 Javascript
jQuery实现点击按钮文字变成input框点击保存变成文字
May 09 Javascript
vue过渡和animate.css结合使用详解
Jun 14 Javascript
原生JavaScript实现的简单放大镜效果示例
Feb 07 Javascript
基于webpack4搭建的react项目框架的方法
Jun 30 Javascript
vue路由传参三种基本方式详解
Dec 09 Javascript
vue基于Teleport实现Modal组件
May 31 Vue.js
浅谈TypeScript 索引签名的理解
Oct 16 Javascript
解决vue-cli webpack打包开启Gzip 报错问题
Jul 24 #Javascript
Vue  webpack 项目自动打包压缩成zip文件的方法
Jul 24 #Javascript
JavaScript面向对象中接口实现方法详解
Jul 24 #Javascript
IE11下处理Promise及Vue的单项数据流问题
Jul 24 #Javascript
微信小程序如何引用外部js,外部样式,公共页面模板
Jul 23 #Javascript
详解Vue中的基本语法和常用指令
Jul 23 #Javascript
js如何获取访问IP、地区、当前操作浏览器
Jul 23 #Javascript
You might like
《PHP编程最快明白》第二讲 数字、浮点、布尔型、字符串和数组
2010/11/01 PHP
php获取指定数量随机字符串的方法
2017/02/06 PHP
php获取给定日期相差天数的方法分析
2017/02/20 PHP
PHP读取XML文件的方法实例总结【DOMDocument及simplexml方法】
2019/09/10 PHP
PHP+Redis事务解决高并发下商品超卖问题(推荐)
2020/08/03 PHP
关于文本限制字数的js代码
2007/04/02 Javascript
cnblogs 代码高亮显示后的代码复制问题解决实现代码
2011/12/14 Javascript
js下拉选择框与输入框联动实现添加选中值到输入框的方法
2015/08/17 Javascript
js+css实现文字散开重组动画特效代码分享
2015/08/21 Javascript
浅谈js构造函数的方法与原型prototype
2016/07/04 Javascript
jQuery文字轮播特效
2017/02/12 Javascript
原生js仿淘宝网商品放大镜效果
2017/02/28 Javascript
详解在Vue中有条件地使用CSS类
2017/09/30 Javascript
Vue props 单向数据流的实现
2018/11/06 Javascript
layui实现数据分页功能
2019/07/27 Javascript
vuejs中父子组件之间通信方法实例详解
2020/01/17 Javascript
小程序卡片切换效果组件wxCardSwiper的实现
2020/02/13 Javascript
js最全的数组的降维5种办法(小结)
2020/04/28 Javascript
[01:06:54]DOTA2-DPC中国联赛 正赛 RNG vs Dragon BO3 第一场 1月24日
2021/03/11 DOTA
Python中使用gzip模块压缩文件的简单教程
2015/04/08 Python
pymongo为mongodb数据库添加索引的方法
2015/05/11 Python
浅析Python中yield关键词的作用与用法
2016/11/29 Python
Android分包MultiDex策略详解
2017/10/30 Python
Django Web开发中django-debug-toolbar的配置以及使用
2018/05/06 Python
python3 xpath和requests应用详解
2020/03/06 Python
X/HTML5 和 XHTML2
2008/10/17 HTML / CSS
值类型与引用类型有什么不同?请举例说明?并分别列举几种相应的数据类型
2015/10/24 面试题
Ruby中的保护方法和私有方法与一般面向对象程序设计语言的一样吗
2013/05/01 面试题
后勤副校长自我鉴定
2013/10/13 职场文书
车间主管岗位职责
2013/11/14 职场文书
学生自我评语大全
2014/04/18 职场文书
关工委先进个人事迹材料
2014/05/23 职场文书
元旦联欢会策划方案
2014/06/11 职场文书
红色经典电影观后感
2015/06/18 职场文书
解决hive中导入text文件遇到的坑
2021/04/07 Python
win10电脑双屏显示一个黑屏怎么办?win10电脑双屏显示一个黑屏解决方法
2022/07/15 数码科技