今天講解的內容是進階模式(Advanced Patterns),如何?靜態方法和屬性,常量還有其他一些知識點。
1.靜態方法和屬性
其實要實現這一功能還是要藉助於閉包。在上一講中的第三種實現方式也使用了閉包,但通過那種實現,內部屬性和方法是執行個體層級的。
var book1=new Book('isbn1','title1','author1');var book2=new Book('isbn2','title2','author2');
alert(book1.getTitle()); //輸出title1
alert(book2.getTitle()); //輸出title2
通過上面的例子可以看出,建立的book1和book2有獨立的私人屬性,對book2的私人屬性賦值不會影響到book1。
那麼怎麼才能實作類別層級的私人屬性和方法呢,這裡除了需要使用閉包之外,還需要使用到匿名函數。代碼如下:代碼1
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'; }; this.getNumOfBooks = function(){
return numOfBooks;
};
// 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() { ... }};
對匿名函數不瞭解的同學可以自己去查閱相關資料。其實上面的代碼等價於:代碼2
var tempBook = function() { // Private static attributes. var numOfBooks = 0; // Private static method. function checkIsbn(isbn) { ... } // Return the constructor. return function(newIsbn, newTitle, newAuthor) { // implements Publication ...
}};
var Book = tempBook();
// Public static method.Book.convertToTitleCase = function(inputString) { ...}; // Public, non-privileged methods.Book.prototype = { display: function() { ... }};
那麼如果再執行之前的代碼會有什麼效果呢:
var book1=new Book('isbn1','title1','author1');var book2=new Book('isbn2','title2','author2');
alert(book1.getNumOfBooks()); //輸出 2
alert(book2.getNumOfBooks()); //輸出 2
為什麼會有這樣的效果呢,其實通過匿名函數加閉包這種組合模式,numOfBooks已經是真正意義上的私人靜態變數的,無論你建立多少個Book的執行個體,系統中也只有一個numOfBooks變數。有人或許疑問,為什麼每建立一個新的Book執行個體的時候,沒有多產生一個numOfBooks呢?
我們從代碼2上可以找到答案,其實我們建立的Book執行個體是執行了tempBook()方法,而不是new tempBook對象!tempBook對象在系統中只會運行一次,所以他內部的numOfBooks變數也就只有一份。每次執行new Book(‘...’,‘...’,‘...’);的時候,其實只是執行了tempBook中的function(newIsbn, newTitle, newAuthor),並沒有建立新的tempBook對象,所有Book執行個體在使用getNumOfBooks方法時調用到的都是同一個numOfBooks。
不知道我的講解是否清楚,希望對大家有協助。
2.常量
其實這部分還真沒什麼好講的,只要你理解了上面的內容,在匿名函數內部實現一個類似於numOfBooks的變數,在整個初始化過程不對他進行修改,也不提供任何修改的方法,同時提供一個類似於getNumOfBooks方法供外部存取它,就ok了,代碼我就懶的寫了。
3.使用封裝的好處和弊端
好處:
可以實現對代碼的控制,公布想公開的隱藏不想公開的資訊。同時也方便你重構代碼,你建立的對象的使用者不會因為你修改內部對象(私人的變數或者方法)而影響別人正常的使用。面相介面編程可以實現代碼之間的松耦合。
弊端:
1.測試內部變數很困難,最好的辦法是通過測試公用方法實現對內部方法的測試。
2.因為複雜的範圍導致代碼調試變的很困難。