封装和信息隐藏
1. 信息隐藏原则
封装是面向对象编程特性之一, javascript 虽然是面向对象的编程语言, 却不像 Java / C++ 那样可以将成员属性声明为私有或是公有. 所以我们只能想办法模拟封装, javascript 也完全有能力完成这一任务.
封装( Encapsulation ), 可以定义为对对象内部数据表现形式和实现细节进行隐藏. 要想访问已经封装的数据只有使用已经定义的函数这一种方法. 也就是通过已定义的接口访问封装的数据.
2. 创建对象的基本模式
有三种:
1) fully exposed 是最简单的一种, 只能提供公有成员.
2) 用 下划线 来标识属性和方法的私有性, 只有提示程序员这是私有的, 但实际上并不是真正私有
3) 闭包创建真正私有的成员, 只能通过特定的方法访问.
(一) 门户大开型, 更夸张点 完全暴露型
这种类型有一个构造器, 属性和方法外部可以访问, 属性需要用 this 关键字来创建.
var Student = function(id, name) {
if(!this.checkId(id)) throw new Error("Invalid Id!");
this.id = id;// 假设 id 应该是私有成员变量
this.name = name;
}
Student.prototype.checkId = function(id) {
// 简单 check id
if(typeof id != 'string' || id.length != 9)
return false;
}
var studentA = new Student();
上面的例子, 包含了一个对学生ID简单check的方法. 但是还是可能通过 studentA.id = "error" 这种方式更改实例的 id 属性, 而且这个 id 没有被 check . 这是我们不希望看到的情况. 为了改善这个问题, 我们会给这个每个属性都定义 get / set 方法. 然后在 set 方法中对参数进行应有的检验工作. 但是话又说回来, 就算有了 get / set 方法, id 属性还是公开的, 并没有改变, 还是可以通过 studentA.id = "error" 来设置.
总结一下这种方法的优缺点:
它易于使用, 初学者很快就能学会, 所有属性方法都公开, 派生子类和进行单元测试也很容易. 缺点是无法保护内部数据, 每个属性的 get / set 方法也会增加许多额外的代码.
(二) 用命名规范区别私有成员, 如 _
其实我认为, 这种方法也是第一种方法的加 get / set 是一个性质的.. 这种方法与上面的方法唯一不同的就是在私有成员的命名时以 _ 开始, 如: _id 和 _checkId().
如上面所说, 这仅仅是一种约定, 实质上还是阻止不了有的人用 studentA._id = "error" .
(三) 作用域 嵌套函数和闭包
var Student = function(id, name) {
var sId, sName;
function checkId() {/* do something */};
this.getId = function() { return sId; }
}
var studentA = new Student('001',)
// 楼主已然崩溃, 本来本文已经写完, 写了很多, 但是CSDN的BLOG系统出了故障.....好了后只恢复到了这里, 心都凉了 - -
书接前文, 简单写点吧.. 私有成员用 var / function checkId() 声明, 而不是前面例子中的 this. 如果想 get / set sId / sName 只有通过 getId() 这样的 特权方法来执行.
特权方法: 它们是公共方法, 但是却可以访问私有成员, 如上面的 getId()
更高级的创建对象方法
(一) 静态成员
var Student = (function() { var studentCount = 0; // static // 下面返回构造函数 return function(sId, sName) { var sId, sName; function checkId() {/* do something */}; this.getId = function() { return sId; }; } })();
这里创建了一个静态变量 studentCount , 静态成员是与类关联的而不是与类的实例关联. 当去判断某属性是否应为静态属性时, 也是依靠这点来的.与前几个例子不同的是, 这里创建了一个加载后直接调用的函数, 返回内联的 Student 的构造函数.
(二) 常量
有了上面静态成员的模拟方法, 常量就容易模拟了, 拿上面的程序为例, 想要让 studentCount 为一个常量, 那么只需定义一个studentCount 的 get() 方法, 而不提供使用者 set()
方法, 那么就相当于模拟了一个常量.
总结:
介绍了几种创建对象的方法, 各有利弊, 用的时候权衡利弊, 作出适当的选择
Javascirpt 不提供内置的封装功能, 所以一切都要靠模拟, 使一切表现得复杂些, 比起 Java, C++, 但是这可能也是Javascript有趣儿的地方.
好了,,本来这篇写了挺多, 竟遇到blog system outage这种事儿, 后面没有第一次写的那么详细了, 请大家多指教 :)