javascript设计模式之:封装

javascript既是一门函数式语言,也是一门面向对象的语言,但是要想在javascript中实现类似于java中的private变量可不是一件显而易见的事。你需要借助它的函数式特性:闭包。

下面的例子演示了如何构建一个javascript对象,没有任何的隐藏和封装。

/**
* 此demo演示了如何在javascript中实现面向对象语言中的信息封装和隐藏。由于javascript不像java那样在语言层面
* 提供了很好的封装支持,因此需要借助一些模式。
*/


Book = function(isbn, title, author) {
if (!isbn) throw new Error('isbn is invalid');
this.isbn = isbn;
this.title = title;
this.author = author;
};

Book.prototype = {
display: function() {
if (!this.isbn) throw new Error('invalid book');
console.log('title :' + this.title + ' author: ' + this.author + ' isbn: ' + this.isbn);
}
};

var book = new Book('0981531652','Actors In Scala', 'Philipp Haller / Frank Sommers');
book.display();

//可以直接的改变book中的成员,使得book不再是合法的book

book.isbn = null;

book.display();


由于没有类似于java中的private关键字,人们发明了“命名规范”来约定所有以下划线'_'开头的变量和方法名都是“私有“的,不应该(注意,是不应该,不是不能)被外部直接调用。


/**
* 在很多javascript库中,通常约定以下划线 '_'开头的变量或者方法名为私有的变量和方法,不应该直接被外部调用
*/

Book = function(isbn,title,author) {
if (!isbn) throw new Error('invalid isbn');
this._isbn =isbn;
this._title = title;
this._author = author;
};


Book.prototype = {
display: function() {
if (!this._isbn) throw new Error('invalid book');
console.log('title :' + this.title + ' author: ' + this.author + ' isbn: ' + this.isbn);
}
};

var book = new Book('0981531652','Actors In Scala', 'Philipp Haller / Frank Sommers');
book.display();
//Book的开发者: oh, _isbn是一个似有变量,你不应该改变直接改变它。
//调用者: 我不是故意的,代码调用层次太深了,我也不知道哪里导致一不小心被设置了。
book._isbn = null;
book.display();


这种办法在每一个程序员都比较自觉的情况下可以发挥效用,但是如果哪个粗心的程序员一不小心。。。

我们需要一种真正的封装!闭包可以做到这一点!什么叫闭包?根据维基百科的定义,一个闭包就是包含自由变量的代码块,这些自由变量定义在代码块所处的上下文中,而不是定义在代码块内部或者全局上下文中。


/**
* 通过javascrit中的closure来实现真正的私有变量。
*/

Book = function(isbn, title, author) {
var _isbn, _title, _author;//局部变量

//局部方法
function checkIsbn(isbn) {
if (!isbn) throw new Error('cannot set invalid isbn');
};

this.getTitle = function() {
return _title;
};

this.setTitle = function(title) {
_title = title;
};

this.getIsbn = function() {
return _isbn;
};

this.setIsbn = function(isbn) {
checkIsbn(isbn);
_isbn = isbn;
};

this.getAuthor = function() {
return _author;
};

this.setAuthor = function(author) {
_author = author;
};

this.setTitle(title);
this.setIsbn(isbn);
this.setAuthor(author);
};

Book.prototype = {
display: function() {
//这里我们不需要在检验isbn是否合法,因为每一次对isbn的更改都必须通过setIsbn的检查
console.log('title :' + this.getTitle() + ' author: ' + this.getAuthor() + ' isbn: ' + this.getIsbn());
}
};

var book = new Book('0981531652','Actors In Scala', 'Philipp Haller / Frank Sommers');
try {
book.setIsbn(null);
}catch(e) {
console.log(e.message);
}

book.display();



在上述例子中,_isbn是Book的构造函数中定义的局部变量,它只在构造函数体内有效;在构造函数中也定义了一个checkIsbn()方法,同样,它也只可以在Book的构造函数体内被调用。getIsbn()引用了_isbn这个局部变量,因此它构成了一个闭包,同时getIsbn()又是Book实例的一个方法,因此可以被外部所调用,同时外部调用者也只能通过getIsbn()来获得_isbn的值。这就实现了真正的隐藏。getIsbn()这个方法既可以访问局部变量,又可以被外部调用,称为对象的特权方法。

但是实现真正的隐藏是需要付出代价的:

1. 首先每一个特权方法(例如isbn的getter和setter)存在于每一个实例中,可能会造成内存占用过多
2. 在存在继承的情况下,子类无法直接服用父类的方法和成员变量,因为javascript使用的是prototype的继承。

在实际中,只有在要求非常严格的封装的情况下才会使用例三的方法,而大部分情况都可以使用命名约定的办法。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值