《JavaScript设计模式》笔记之第三章:封装和信息隐藏
第三章
创建对象的基本模式
方法二:加强对属性的检测
方法三:添加取值器和赋值器
方法四:用命名规范区别私用成员
方法五:用闭包实现私有成员
方法六:常量
小结:上面所有方法都是返回值都是构造函数来的,如果我们不需要构造函数,就可以利用单体模式,后面章节会说。此外,封装的最好用处就是防止别人修改你的数据,最大的缺点就是麻烦~
一个综合例子:
方法一:门户大开型
var Book = function(isbn, title, author) {
if(isbn == undefined ) throw new Error ('Book constructor requires an isbn.');
this.isbn = isbn;
this.title = title || 'No title specified';
this.author = author || 'No author specified';
}
Book.prototype.display = function () {
...
};
特点:所有属性都是公开的,并且没有有效的验证数据的有效性
方法二:加强对属性的检测
var Book = function(isbn, title, author) {
if( !this.checkIsbn(isbn)) throw new Error( 'Book: Invalid ISBN.');
this.isbn = isbn;
this.title = title || 'No title specified';
this.author = author || 'No author specified';
}
Book.prototype = {
checkIsbn : function (isbn) {
if(isbn == undefined || typeof isbn != 'string') {
return false ;
}
isbn = isbn.replace(/-/ . ''); // Remove dashes.
if(isbn.length != 10 && isbn.length != 13) {
return false ;
}
var sum = 0;
if(isbn.length === 10) { // 10 digit ISBN.
If( !isbn.match(\^\ d{9 }\)) { // Ensure characters 1 through 9 are digits.
return false ;
}
for(var i = 0; i < 9; i ++) {
sum += isbn.charAt(i) * (10 - i);
}
var checksum = sum % 11;
if(checksum === 10) checksum = 'X' ;
if(isbn.charAt(9 ) != checksum) {
return false ;
}
}
else { // 13 digit ISBN.
if(! isbn.match(\ ^\d{12} \)) { // Ensure characters 1 through 12 are digits.
return false ;
}
for(var i = 0; i < 12; i ++) {
sum += isbn.charAt(i) * ((i % 2 === 0) ? 1 : 3);
}
var checksum = sum % 10;
if(isbn.charAt(12 ) != checksum) {
return false ;
}
}
return true ; // All tests passed.
},
display: function() {
...
}
};
特点:在Book.prototype上添加了一个checkIsbn函数,可以在实例化的时候检查数据的有效性
缺点:还没有解决属性暴露在外面的问题
方法三:添加取值器和赋值器
var Book = function(isbn, title, author) { // implements Publication
this.setIsbn(isbn);
this.setTitle(title);
this.setAuthor(author);
}
Book.prototype = {
checkIsbn : function (isbn) {
...
},
getIsbn: function() {
return this .isbn;
},
setIsbn: function(isbn) {
if(! this.checkIsbn(isbn)) throw new Error('Book: Invalid ISBN.' );
this.isbn = isbn;
},
getTitle : function () {
return this .title;
},
setTitle : function (title) {
this.title = title || 'No title specified';
},
getAuthor : function () {
return this .author;
},
setAuthor : function (author) {
this.author = author || 'No author specified';
},
display: function() {
...
}
};
特点:上面方法的一个改进,通过赋值器验证数据的有效性
缺点:赋值器和取值器添加了额外的代码,而且属性还是暴露在外面
方法四:用命名规范区别私用成员
var Book = function(isbn, title, author) { // implements Publication
this.setIsbn(isbn);
this.setTitle(title);
this.setAuthor(author);
}
Book.prototype = {
_checkIsbn : function (isbn) {
...
},
getIsbn: function() {
return this ._isbn;
},
setIsbn: function(isbn) {
if(! this._checkIsbn(isbn)) throw new Error('Book: Invalid ISBN.' );
this._isbn = isbn;
},
getTitle : function () {
return this ._title;
},
setTitle : function (title) {
this._title = title || 'No title specified';
},
getAuthor : function () {
return this ._author;
},
setAuthor : function (author) {
this._author = author || 'No author specified';
},
display: function() {
...
}
};
特点:其实也就是把属性的名称下面加了一个下划线"_"。在属性名前加一个下划线是说明这是私有变量的一种约定,使用的人就可以清楚不应该修改这个属性。
缺点:这只是一种约定,可以让别人不误用,但是如果别人有意要修改这私有属性,也是可以的。
方法五:用闭包实现私有成员
var Book = function(newIsbn, newTitle, newAuthor) { // implements Publication
// Private attributes.
var isbn, title, author;
// Private method.
function checkIsbn(isbn) {
...
}
// Privileged methods.
this.getIsbn = function () {
return isbn;
};
this.setIsbn = function (newIsbn) {
if(! checkIsbn(newIsbn)) throw new Error ('Book: Invalid ISBN.');
isbn = newIsbn;
};
this.getTitle = function () {
return title;
};
this.setTitle = function (newTitle) {
title = newTitle || 'No title specified';
};
this.getAuthor = function () {
return author;
};
this.setAuthor = function (newAuthor) {
author = newAuthor || 'No author specified';
};
// Constructor code.
this.setIsbn(newIsbn);
this.setTitle(newTitle);
this.setAuthor(newAuthor);
};
// Public, non-privileged methods.
Book.prototype = {
display: function() {
...
}
};
特点:私有变量用var声明而不用this赋给对象,使对象不能直接操作私有变量。此外,用this.method这样的方法定义特权方法(赋值器等),使用于可以通过特权方法操作私有变量,公共方法就定义在Book.prototype中。
缺点:特权方法的增加会耗费更多的内存,不像第一种方法一样把所有方法都放在prototype里面。并且以后的子类都不能访问这这样定义的私有方法
方法五:静态方法和属性
var Book = (function() {
// Private static attributes.
var numOfBooks = 0 ;
// Private static method.
function checkIsbn(isbn) {
...
}
// Return the constructor.
return function(newIsbn, newTitle, newAuthor) { // implements Publication
// Private attributes.
var isbn, title, author;
// Privileged methods.
this.getIsbn = function() {
return isbn;
};
this.setIsbn = function(newIsbn) {
if(! checkIsbn(newIsbn)) throw new Error ('Book: Invalid ISBN.');
isbn = newIsbn;
};
this.getTitle = function() {
return title;
};
this.setTitle = function(newTitle) {
title = newTitle || 'No title specified';
};
this.getAuthor = function() {
return author;
};
this.setAuthor = function(newAuthor) {
author = newAuthor || 'No author specified';
};
// Constructor code.
numOfBooks ++; // Keep track of how many Books have been instantiated
// with the private static attribute.
if(numOfBooks > 50) throw new Error( 'Book: Only 50 instances of Book can be '
+ 'created.' );
this.setIsbn(newIsbn);
this.setTitle(newTitle);
this.setAuthor(newAuthor);
}
})();
// Public static method.
Book.convertToTitleCase = function (inputString) {
...
};
// Public, non-privileged methods.
Book.prototype = {
display: function() {
...
}
};
特点:这种方法最大一个特点就是构造函数是通过立即调用函数返回出来的,这样外层的这个立即调用函数就形成了一个闭包,在里面我们就可以声明一些静态私有的变量或者方法了。
方法六:常量
var Class = (function() {
// Private static attributes.
var constants = {
UPPER_BOUND : 100 ,
LOWER_BOUND : -100
}
// Constructor
var ctor = function(constructorArgument) {
...
};
// Privileged static method.
ctor.getConstant(name) {
return constants[name];
}
...
// Return the constructor.
return ctor;
})();
/* Usage. */
Class.getConstant( 'UPPER_BOUND');
特点:同样上面的那种方法,不同的是只设置一个取值器,不设置赋值器,就可以模仿常量了。
小结:上面所有方法都是返回值都是构造函数来的,如果我们不需要构造函数,就可以利用单体模式,后面章节会说。此外,封装的最好用处就是防止别人修改你的数据,最大的缺点就是麻烦~
一个综合例子:
<html> <body> <script> var Student = (function(){ var _count = 0;//私有静态变量 var constants = {//常量 MAX_NUM : 1000 }; function Student(name, age) { var _name;//私有变量 var _age;//私有变量 var checkAge = function(age) {//私有方法 if(typeof(age) != 'number') { return false; } else { return true; } }; this.setName = function(name) {//特权方法 _name = name; }; this.setAge = function(age) {//特权方法 if(!checkAge(age)) throw new Error("age should be number"); _age = age; }; this.getName = function() {//特权方法 return _name; }; this.getAge = function() {//特权方法 return _age; }; this.setName(name); this.setAge(age); _count++; } Student.getConstant = function(name) {//公共静态方法 return constants[name]; }; Student.getCount = function() {//公共静态方法 return _count; }; Student.prototype.showName = function() {//公共非特权方法 alert(this.getName()); }; Student.prototype.showAge = function() {//公共非特权方法 alert(this.getAge()); }; return Student; })(); //使用 var Mark = new Student('Mark', 18); var Sally = new Student('Sally', 19); </script></body>