封裝之利
- 保證內部數(shù)據(jù)完整浆熔,易于重構
- 弱化模塊間耦合邢疙,提高對象可重用性
封裝之弊
- 單元測試困難暖哨,錯誤調試困難
- 過度封裝會損失類的靈活性
- 對新手不友好
創(chuàng)建對象的基本模式
需求:創(chuàng)建一個存儲一本書的類目派,并為其實現(xiàn)一個以HTML形式顯示數(shù)據(jù)的方法然低。你只負責創(chuàng)建這個類仔役,別人會創(chuàng)建和使用其實例掷伙。
// 使用方式
var theHobbit = new Book('0-395-07122-4','The Hobbit','J.R.R. Tolkien');
theHobbit.display() // 以HTML形式顯示數(shù)據(jù)
門戶大開型對象
var Publication = new interface('Publication', ['getIsbn', 'setIsbn', 'getTitle', 'setTitle', 'getAuthor',
'setAuthor', 'display'
])
var Book = function (isbn, title, author) {
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 () {
// ...
}
}
上述代碼定義了一個接口,那么其他程序員就應該只能使用接口中定義的屬性和方法又兵。
這是門戶大開型對象創(chuàng)建方式所能得到的最好結果任柜。一個明確定義的接口,一些數(shù)據(jù)的賦值器和取值器沛厨,及一些檢驗方法宙地。
缺點就是雖然我們定義了賦值器方法,但是這些屬性仍然是公開的逆皮、可以直接設置的宅粥。
命名規(guī)范區(qū)分私有成員
var Book = function (isbn, title, author) {
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 () {
// ...
}
}
這是一種程序員約定俗成的方法,加下劃線表示私有變量电谣。但是JS中跟本沒有私有變量的定義秽梅,只能說是總所周知的命名規(guī)范抹蚀。缺點也很明顯,既然是約定企垦,那么只有在遵守時才有效果环壤。
閉包實現(xiàn)私用成員
var Book = function (newIsbn, newTitle, newAuthor) {
//私有屬性
var isbn, title, author;
// 私有方法
function checkIsbn(isbn) {
// ...
};
// 特權方法
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';
};
// 構造函數(shù) 賦值
this.setIsbn(newIsbn);
this.setTitle(newTitle);
this.setAuthor(newAuthor);
};
// 公共方法
Book.prototype = {
display() {
// ...
}
}
這種方式創(chuàng)建的對象具有真正的私有屬性,解決了可以直接取值賦值的問題钞诡。缺點是會耗費更多的內存郑现,而且不利于派生子類。在JS中荧降,用閉包實現(xiàn)私用成員導致的派生問題被稱為”繼承破壞封裝“接箫。
MORE
靜態(tài)成員
作用域和閉包可用于創(chuàng)建靜態(tài)成員。大多數(shù)方法和屬性所關聯(lián)的是類的實例朵诫,而靜態(tài)成員所關聯(lián)的是類本身辛友。
- 一個栗子
var Book = (function () {
// 私有靜態(tài)屬性
var numOfBookes = 0;
// 私有靜態(tài)方法
function checkIsbn(isbn) {
// ...
};
// 返回構造器
return function (newIsbn, newTitle, newAuthor) {
// 私有屬性
var isbn, title, author;
// 特權方法
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';
};
numOfBookes++;
if (numOfBookes > 50) {
throw new Error('Book:Only 50 instance of book can be created.')
}
// 賦值
this.setIsbn(newIsbn);
this.setTitle(newTitle);
this.setAuthor(newAuthor);
}
})();
// 公共靜態(tài)方法
Book.converToTitleCase = function (inputString) {
// ...
};
// 公共方法 非特權
Book.prototype = {
display:function() {
// ...
}
}
閉包,返回一個構造器拗窃,私用成員和特權成員仍然在構造器中瞎领。但是閉包中可以訪問靜態(tài)成員。優(yōu)點在于所有的靜態(tài)成員只會存在一份随夸,這樣大大減小了內存的消耗九默。
q:如何判斷是否設計成靜態(tài)成員?
a:一般情況下宾毒,我們只需要看一個屬性或者方法是否需要訪問實例數(shù)據(jù)驼修。如果不需要,那么設計成靜態(tài)成員會更有效率诈铛。
常量
常量的取值
var Class = (function () {
var constans = {
UPPER_BOUND: 100,
LOWER_BOUND: -100
};
var ctor = function () {
// ...
}
// 特權靜態(tài)方法
ctor.getConstans = function (name) {
return constans[name];
}
return ctor
})()
// 取值
Class.getConstans('UPPER_BOUND')