利用闭包创建一个受保护的变量空间,可以实现公用、私用和特权成员,以及静态类成员和常量。
封装可以让对象的实现细节对其他对象保密以降低对象之间的耦合程度。
可以保持数据的完整性并对其修改方式加以约束。
封装是面向对象设计的基石。
接口在封装过程中发挥的作用:
接口提供了一份记载着可供公众访问的方法的契约。在实现类时应避免公开未定义在接口中的方法。否则其他对象可能会对那些并不属于接口的方法产生依赖,而这是不安全的。因为这些方法随时都可能发生改变或被删除。
一个理想的软件系统应该为所有类定义接口。这些类只向外界提供它们实现的接口中规定的方法,其他方法留着自用
创建对象的基本模式:
1. 门户大开型对象 2. 用命名规范区别私有成员 3. 用闭包实现私有成员
1. 门户大开型对象 var Publication = new Interface('Publication', ['getIsbn', 'setIsbn', 'setTitle', 'getTitle', 'setAuthor', 'getAuthor', 'display']); 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(!checkIsbn(isbn)) throw new Error('不合法的isbn!'); }, //... display: function() { //公有方法 //... } }; 2. 用命名规范区别私有成员 var Publication = new Interface('Publication', ['getIsbn', 'setIsbn', 'setTitle', 'getTitle', 'setAuthor', 'getAuthor', 'display']); var Book = function(isbn, title, author) { //implements Publication this.setIsbn(isbn); this.setTitle(title); this.setAuthor(author); }; //在属性和方法前加下划线以示私有属性 Book.prototype = { _checkIsbn: function(isbn) { //private method //... }, getIsbn: function() { return this._isbn; //private property }, setIsbn: function(isbn) { if(!checkIsbn(isbn)) throw new Error('不合法的isbn!'); this._isbn = isbn; }, //... display: function() { //... } }; 3. 用闭包实现私有成员 var Publication = new Interface('Publication', ['getIsbn', 'setIsbn', 'setTitle', 'getTitle', 'setAuthor', 'getAuthor', 'display']); var Book = function(isbn0, title0, author0) { //implements Publication var isbn, title, author; //private property function checkIsbn(isbn) { //private method //... } //特权方法 this.getIsbn = function() { return isbn; }; this.setIsbn = function(isbn0) { if(!checkIsbn(isbn)) throw new Error('不合法的isbn!'); isbn = isbn0; }; //... this.setIsbn(isbn0); this.setTitle(title0); this.setAuthor(author0); }; //公有方法 Book.prototype = { // display: function() { //... } };
特权方法与公有方法之间的区别:
任何不需要直接访问私有属性的公开方法建议放在Book.prototype中。
只有那些需要直接访问私有属性的方法才应该设计成为特权方法。特权方法太多会占用过多内存,因为每个对象实例都包含了所有特权方法的新副本。
静态属性和方法:
var Book = (function() { var numOfBooks = 0; //private static property function checkIsbn(isbn) { //private static method //... } //return the constructor //普通函数变成内嵌函数,并作为返回值赋给变量Book return function(isbn0, title0, author0) { //implements Publication var isbn, title, author; //private property //特权方法 this.getIsbn = function() { return isbn; }; this.setIsbn = function(isbn0) { if(!checkIsbn(isbn)) throw new Error('不合法的isbn!'); isbn = isbn0; }; //... numOfBooks++; this.setIsbn(isbn0); this.setTitle(title0); this.setAuthor(author0); } })(); //public static method Book.convertToTitleCase = function(str) { //... }; //public methods Book.prototype = { display: function() { //... } };
实例化Book时调用的是内嵌函数,外层函数只是用于创建一个可以用来存放静态私有成员的闭包。
判断一个私有方法是否应该设计成为静态方法:一条经验法则是看它是否需要访问任何实例数据。如果不需要那么设计为静态方法会更高效(内存占用方面),因为它只会在内存中创建一份。
常量
用只有取值器,没有赋值器的私有属性来模仿常量。