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基础的动画教程,直观易懂
Jan 10 Javascript
qTip2 精致的基于jQuery提示信息插件
Feb 17 Javascript
jQuery中获取checkbox选中项等操作及注意事项
Nov 24 Javascript
自己封装的javascript事件队列函数版
Jun 12 Javascript
jQuery后代选择器用法实例
Dec 23 Javascript
纯Javascript实现ping功能的方法
Mar 20 Javascript
JQuery分屏指示器图片轮换效果实例
May 21 Javascript
JavaScript中使用concat()方法拼接字符串的教程
Jun 06 Javascript
AngularJS控制器详解及示例代码
Aug 16 Javascript
值得分享的Bootstrap Table使用教程
Nov 23 Javascript
12306 刷票脚本及稳固刷票脚本(防挂)
Jan 04 Javascript
vue-cli 脚手架基于Nightwatch的端到端测试环境的过程
Sep 30 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
暴雪前总裁遗憾:没尽早追赶Dota 取消星际争霸幽灵
2020/03/08 星际争霸
php实现的遍历文件夹下所有文件,编辑删除
2010/01/05 PHP
一个PHP的ZIP压缩类分享
2014/05/04 PHP
PHP基于php_imagick_st-Q8.dll实现JPG合成GIF图片的方法
2014/07/11 PHP
WordPress中用于获取文章信息以及分类链接的函数用法
2015/12/18 PHP
解析WordPress中控制用户登陆和判断用户登陆的PHP函数
2016/03/01 PHP
php实现的简单中文验证码功能示例
2017/01/03 PHP
Javascript typeof 用法
2008/12/28 Javascript
基于jquery插件制作左右按钮与标题文字图片切换效果
2013/11/07 Javascript
使用jquery动态加载Js文件和Css文件
2015/10/24 Javascript
JavaScript中文件上传API详解
2016/04/01 Javascript
JS产生随机数的几个用法详解
2016/06/22 Javascript
浅谈javascript中的加减时间
2016/07/12 Javascript
如何处理JSON中的特殊字符
2016/11/30 Javascript
javascript阻止事件冒泡和浏览器的默认行为
2017/01/21 Javascript
JS开发中基本数据类型具体有哪几种
2017/10/19 Javascript
详解node字体压缩插件font-spider的用法
2018/09/28 Javascript
详解使用Nuxt.js快速搭建服务端渲染(SSR)应用
2019/03/13 Javascript
JS中的算法与数据结构之字典(Dictionary)实例详解
2019/08/20 Javascript
JS实现网页端猜数字小游戏
2020/03/06 Javascript
JS中类的静态方法,静态变量,实例方法,实例变量区别与用法实例分析
2020/03/14 Javascript
Node.js API详解之 util模块用法实例分析
2020/05/09 Javascript
[02:20]DOTA2亚洲邀请赛 EHOME战队出场宣传片
2015/02/07 DOTA
详解python中 os._exit() 和 sys.exit(), exit(0)和exit(1) 的用法和区别
2017/06/23 Python
Python实现将文本生成二维码的方法示例
2017/07/18 Python
对python 命令的-u参数详解
2018/12/03 Python
python操作redis数据库的三种方法
2020/09/10 Python
pycharm 代码自动补全的实现方法(图文)
2020/09/18 Python
python上下文管理的使用场景实例讲解
2021/03/03 Python
使用CSS3的背景渐变Text Gradient 创建文字颜色渐变
2014/08/19 HTML / CSS
豪华复古化妆:Besame Cosmetics
2019/09/06 全球购物
应届生煤化工求职信
2013/10/21 职场文书
会计学自荐信
2014/06/03 职场文书
战马观后感
2015/06/08 职场文书
会议室管理制度范本
2015/08/06 职场文书
python ConfigParser库的使用及遇到的坑
2022/02/12 Python