以下內(nèi)容來自張榮銘《JavaScript設(shè)計模式》第2章。
兩種編程風(fēng)格 -- 面向過程與面向?qū)ο蟆?/p>
面向?qū)ο缶幊淌侵笇⑿枨蟪橄蟪梢粋€對象纯趋,然后針對這個對象分析其特征(屬性)與動作(方法)嗅辣,這個對象我們稱之為類撼泛。
面向?qū)ο缶幊趟枷胫校渲幸粋€特點就是封裝澡谭,即把需要的功能放在一個對象里愿题,便于管理。但由于JavaScript屬于解釋性的弱類語言蛙奖,沒有經(jīng)典強類型語言中那種通過class
等關(guān)鍵字實現(xiàn)的類的封裝方式潘酗。
1 封裝
1-1 創(chuàng)建一個類
首先聲明一個函數(shù),保存在一個變量內(nèi)雁仲。按照編程習(xí)慣仔夺,代表類的首字母一般會用大寫;
然后這個函數(shù)(類)的內(nèi)部通過對this
(函數(shù)內(nèi)部自帶的一個變量攒砖,用于指向當(dāng)前這個對象)變量添加屬性或者方法來實現(xiàn)對類添加屬性或者方法缸兔。
也可以通過在類的原型(類也是一個對象,也有原型prototype
)上添加屬性和方法吹艇。兩種方法:一種是一一為原型對象屬性賦值惰蜜,另一種則是將一個對象賦值給類的原型對象。但兩種方法不建議混用掐暮。
var Book = function (id蝎抽, bookname, price) {
this.id = id;
this.bookname = bookname;
this.price = price;
}
Book.prototype.display = function () {};
// 或者
Book.prototype = {
display: function () {}
}
使用:
用 new
關(guān)鍵字來實例化(創(chuàng)建)新的對象。使用實例化對象的屬性或者方法時樟结,可以通過點語法訪問养交。
JavaScript是一種基于原型prototype
的語言,所以在創(chuàng)建一個對象時都有一個原型prototype
用于指向其繼承的屬性瓢宦、方法碎连。這樣通過prototype
繼承的方法并不是對象自身的,在使用這些方法時需要通過prototype
一級一級查找得到驮履。
通過 this
添加的屬性鱼辙、方法是在當(dāng)前對象上添加的,是該對象自身擁有的玫镐,通過類創(chuàng)建一個新對象時倒戏,this
指向的屬性和方法都會得到相應(yīng)的創(chuàng)建。通過prototype
繼承的屬性或者方法是每個對象通過prototype
訪問到恐似,每次通過類創(chuàng)建一個新對象時這些屬性和方法不會再次創(chuàng)建杜跷。
1-2 constructor屬性
當(dāng)創(chuàng)建一個函數(shù)或者對象時都會為其創(chuàng)建一個原型對象prototype
,在prototype
對象中會創(chuàng)建一個constructor
屬性矫夷。constructor
屬性指向的就是擁有整個原型對象的函數(shù)或?qū)ο蟆?/p>
1-3 屬性與方法封裝
通過JS函數(shù)級作用域的特征來實現(xiàn)在函數(shù)內(nèi)部創(chuàng)建外界訪問不到的私有化變量和私有化方法葛闷。
// 私有屬性 與 私有方法 , 特權(quán)方法双藕, 對象公有屬性和對象共有方法淑趾, 構(gòu)造器
var Book = functiion (id, name, price) {
// 私有屬性
var num = 1;
// 私有方法
function checkId () {};
// 特權(quán)方法
this.getName = function () {};
this.getPrice = function () {};
this.setName = function () {};
this.setPrice = function () {};
// 對象公有屬性
this.id = id;
// 對象公有方法
this.copy = function () {};
// 構(gòu)造器
this.setName(name);
this.setPrice(price);
}
通過new
關(guān)鍵字實例化對象時,由于對類執(zhí)行一次忧陪,所以類的內(nèi)部 this
上定義的屬性和方法復(fù)制到新創(chuàng)建的對象上扣泊,成為對象公有化的屬性和方法,而其中的一些方法能訪問到類的私有屬性和方法嘶摊。
通過new
關(guān)鍵字創(chuàng)建的新對象旷赖,無法獲取類外面通過點語法添加的屬性和方法,但是可以通過類來使用更卒。因此在類外面通過點語法定義的屬性以及方法被稱為類的靜態(tài)共有屬性和類的靜態(tài)共有方法。
而類通過prototype
創(chuàng)建的屬性或者方法在類實例的對象中是可以通過this
訪問到的稚照,所有prototype
對象中的屬性和方法稱為共有屬性和共有方法蹂空。
// 類靜態(tài)公有屬性(對象不能訪問)
Book.isChinese = true;
// 類靜態(tài)公有方法(對象不能訪問)
Book.resetTime = function () {
console.log('new-Time');
}
Book.prototype = {
// 公有屬性
isJSBook: false,
// 公有方法
display: function () {}
}
聽過new
關(guān)鍵字創(chuàng)建的對象實質(zhì)是對新對象this
的不斷賦值,并將prototype
指向類的prototype
所指向的對象果录,而類的構(gòu)造函數(shù)外面通過點語法定義的屬性方法是不會添加到新創(chuàng)建的對象上去的上枕。因此想要在新創(chuàng)建的對象中使用 isChinese
需要通過Book
類,而不能通過this
弱恒, 如 Book.isChinese
辨萍。
通過類的原型prototype
上定義的屬性在新對象里可以直接使用點方法訪問。原因:新對象的prototype
和類的prototype
指向的是同一個對象。
例:
var b = new Book(11, 'javascript設(shè)計模式', 50);
console.log(b.num); // undefined
console.log(b.isJSBook); // false
console.log(b.id); // 11
console.log(b.isChinese); // undefined
console.log(Book.isChinese); // true
Book.resetTime(); // new-Time
1-4 閉包
閉包是有權(quán)訪問另外一個函數(shù)作用域中變量的函數(shù)锈玉,即在一個函數(shù)內(nèi)部創(chuàng)建另外一個函數(shù)爪飘。
// 利用閉包實現(xiàn)
var Book = (function () {
// 靜態(tài)私有變量
var bookNum = 0;
// 靜態(tài)私有方法
function checkBook (name) { };
// 返回構(gòu)造函數(shù)
return function (newId, newName, newPrice) {
// 私有變量
var name, price;
// 私有方法
function checkID (id) { };
// 特權(quán)方法
this.getName = function () {};
this.getPrice = function () {};
this.setName = function () {};
this.setPrice = function () {};
// 公有屬性
this.id = newId;
// 公有方法
this.copy = function () {};
bookNum++;
if (bookNum > 100) throw new Error('我們僅出版100本書');
// 構(gòu)造器
this.setName(name);
this.setPrice(price);
}
})();
Book.prototype = {
// 靜態(tài)公有屬性
isJSBook: false;
// 靜態(tài)公有方法
display: function () {}
}
// 利用閉包實現(xiàn)
var Book = (function () {
// 靜態(tài)私有變量
var bookNum = 0拉背;
// 靜態(tài)私有方法
function checkBook (name) { };
// 創(chuàng)建類
function book(newId, newName, newPrice) {
// 私有變量
var name, price;
// 私有方法
function checkID (id) { };
// 特權(quán)方法
this.getName = function () {};
this.getPrice = function () {};
this.setName = function () {};
this.setPrice = function () {};
// 公有屬性
this.id = newId;
// 公有方法
this.copy = function () {};
bookNum++;
if (bookNum > 100) throw new Error('我們僅出版100本書');
// 構(gòu)造器
this.setName(name);
this.setPrice(price);
}
// 構(gòu)建原型
_book.prototype = {
// 靜態(tài)公有屬性
isJSBook: false;
// 靜態(tài)公有方法
display: function () {}
}
// 返回類
return _book;
})();
1-5 安全模式
// 圖書安全類
var Book = function (title, time, type) {
// 判斷執(zhí)行過程中师崎, this是否是當(dāng)前這個對象(如果是說明是用new創(chuàng)建的)
if (this instanceof Book) {
this.title = title;
this.time = time;
this.type = type;
// 否則重新創(chuàng)建這個對象
} else {
return new Book(title, time, type);
}
}
var book = Book('JavaScript', '2014', 'js');