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 相关文章推荐
JavaScript 闭包在封装函数时的简单分析
Nov 28 Javascript
关于JavaScript命名空间的一些心得
Jun 07 Javascript
AngularJS实现按钮提示与点击变色效果
Sep 07 Javascript
浅谈JavaScript异步编程
Jan 20 Javascript
ES6中Proxy与Reflect实现重载(overload)的方法
Mar 30 Javascript
angularjs1.5 组件内用函数向外传值的实例
Sep 30 Javascript
使用react render props实现倒计时的示例代码
Dec 06 Javascript
vue获取时间戳转换为日期格式代码实例
Apr 17 Javascript
vue 进阶之实现父子组件间的传值
Apr 26 Javascript
微信小程序 如何保持登录状态
Aug 16 Javascript
React 父子组件通信的实现方法
Dec 05 Javascript
一篇文章带你从零快速上手Rollup
Sep 07 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
DISCUZ 分页代码
2007/01/02 PHP
PHP面向对象详解(三)
2015/12/07 PHP
php版微信自动获取收货地址api用法示例
2016/09/22 PHP
php each 返回数组中当前的键值对并将数组指针向前移动一步实例
2016/11/22 PHP
一段利用WSH获取登录时间的jscript代码
2008/05/11 Javascript
JS、CSS加载中的小问题探讨
2013/11/26 Javascript
jQuery实现购物车数字加减效果
2015/03/14 Javascript
原生JS封装ajax 传json,str,excel文件上传提交表单(推荐)
2016/06/21 Javascript
AngularJS包括详解及示例代码
2016/08/17 Javascript
使用jQuery的load方法设计动态加载及解决被加载页面js失效问题
2017/03/01 Javascript
浅谈用Webpack路径压缩图片上传尺寸获取的问题
2018/02/22 Javascript
JavaScript基础心法 深浅拷贝(浅拷贝和深拷贝)
2018/03/05 Javascript
JS Object.preventExtensions(),Object.seal()与Object.freeze()用法实例分析
2018/08/25 Javascript
nodejs初始化init的示例代码
2018/10/10 NodeJs
JavaScrip数组去重操作实例小结
2019/06/20 Javascript
[01:02:30]Mineski vs Secret 2019国际邀请赛淘汰赛 败者组 BO3 第三场 8.22
2019/09/05 DOTA
linux下python抓屏实现方法
2015/05/22 Python
Python3 XML 获取雅虎天气的实现方法
2018/02/01 Python
在IPython中执行Python程序文件的示例
2018/11/01 Python
如何在Django中设置定时任务的方法示例
2019/01/18 Python
Python批量删除只保留最近几天table的代码实例
2019/04/01 Python
Python制作微信好友背景墙教程(附完整代码)
2019/07/17 Python
python遍历文件目录、批量处理同类文件
2019/08/31 Python
Django admin 实现search_fields精确查询实例
2020/03/30 Python
Django数据结果集序列化并展示实现过程
2020/04/22 Python
利用python3筛选excel中特定的行(行值满足某个条件/行值属于某个集合)
2020/09/04 Python
python FTP编程基础入门
2021/02/27 Python
HTML5的Geolocation地理位置定位API使用教程
2016/05/12 HTML / CSS
什么是WEB控件?使用WEB控件有哪些优势?
2012/01/21 面试题
后勤人员自我评价怎么写
2013/09/19 职场文书
写自荐信的七个技巧
2013/10/15 职场文书
校运会广播稿100字
2014/01/27 职场文书
小班评语大全
2014/05/04 职场文书
乡镇党的群众路线教育实践活动领导班子对照检查材料
2014/09/25 职场文书
土地租赁协议书
2015/01/29 职场文书
Mysql官方性能测试工具mysqlslap的使用简介
2021/05/21 MySQL