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 相关文章推荐
自己开发Dojo的建议框架
Sep 24 Javascript
JS与框架页的操作代码
Jan 17 Javascript
jquery 选取方法都有哪些
May 18 Javascript
控制文字内容的显示与隐藏示例
Jun 11 Javascript
JavaScript实现从数组中选出和等于固定值的n个数
Sep 03 Javascript
Jquery基础教程之DOM操作
Aug 19 Javascript
jquery+css实现动感的图片切换效果
Nov 25 Javascript
BootStrap中Tab页签切换实例代码
May 30 Javascript
深入解析Javascript闭包的功能及实现方法
Jul 10 Javascript
vue项目设置scrollTop不起作用(总结)
Dec 21 Javascript
vue实现图片上传功能
May 28 Javascript
JavaScript中isPrototypeOf函数
Nov 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
set_include_path和get_include_path使用及注意事项
2013/02/02 PHP
Php header()函数语法及使用代码
2013/11/04 PHP
jquery HotKeys轻松搞定键盘事件代码
2008/08/30 Javascript
js apply/call/caller/callee/bind使用方法与区别分析
2009/10/28 Javascript
JS 自定义函数缺省值的设置方法
2010/05/05 Javascript
轻松创建nodejs服务器(5):事件处理程序
2014/12/18 NodeJs
再谈JavaScript线程
2015/07/10 Javascript
jquery 重写 ajax提交并判断权限后 使用load方法报错解决方法
2016/01/19 Javascript
JavaScript+html5 canvas制作的百花齐放效果完整实例
2016/01/26 Javascript
jquery.validate使用详解
2016/06/02 Javascript
jquery easyui validatebox remote的使用详解
2016/11/09 Javascript
ajax级联菜单实现方法实例分析
2016/11/28 Javascript
详解vue渲染从后台获取的json数据
2017/07/06 Javascript
vue 怎么创建组件及组件使用方法
2017/07/27 Javascript
javaScript字符串工具类StringUtils详解
2017/12/08 Javascript
解决vant title-active-color与title-inactive-color不生效问题
2020/11/03 Javascript
[06:45]DOTA2-DPC中国联赛 正赛 Magma vs LBZS 选手采访
2021/03/11 DOTA
Django 使用Ajax进行前后台交互的示例讲解
2018/05/28 Python
python中datetime模块中strftime/strptime函数的使用
2018/07/03 Python
Django框架中间件定义与使用方法案例分析
2019/11/28 Python
Python获取二维数组的行列数的2种方法
2020/02/11 Python
使用python的pyplot绘制函数实例
2020/02/13 Python
python 如何将office文件转换为PDF
2020/09/22 Python
HTML5 video 事件应用示例
2014/09/11 HTML / CSS
学生处主任岗位职责
2013/12/01 职场文书
生产部统计员岗位职责
2014/01/05 职场文书
生产部管理制度
2014/01/31 职场文书
老同学聚会感言
2014/02/23 职场文书
低碳生活的宣传标语
2014/06/23 职场文书
关于晚自习早退的检讨书
2014/09/13 职场文书
街道党风廉政建设调研报告
2015/01/01 职场文书
2015年度电厂个人工作总结
2015/05/13 职场文书
于丹讲座视频观后感
2015/06/15 职场文书
用Python selenium实现淘宝抢单机器人
2021/06/18 Python
node快速搭建后台的实现步骤
2022/02/18 NodeJs
Java实现扫雷游戏详细代码讲解
2022/05/25 Java/Android