javascript
JavaScript --- [学习笔记]观察者模式 理解对象 工厂模式 构造函数模式
說(shuō)明
- 本系列(JS基礎(chǔ)梳理)為后面TCP的模擬實(shí)現(xiàn)做準(zhǔn)備
- 本篇的主要內(nèi)容: 觀察者模式、工廠(chǎng)模式、構(gòu)造函數(shù)模式 和 對(duì)對(duì)象的理解
1. 觀察者模式
- 參考JavaScript設(shè)計(jì)模式
1.1 消息注冊(cè)方法
“將訂閱者注冊(cè)的消息推入到消息隊(duì)列中”
[算法思路] :
1.2 發(fā)布消息方法
“對(duì)于發(fā)布消息方法,其功能是當(dāng)觀察者發(fā)布一個(gè)消息時(shí)將所有訂閱者的消息一次執(zhí)行”
fire: function(type, args) {// 如果該消息沒(méi)有被注冊(cè),則返回if(!__message[type])return;// 定義消息信息var events = {type: type,args: args || {}},i = 0,len = __message[type].length;// 遍歷消息動(dòng)作for(; i < len; i++) {// 依次執(zhí)行注冊(cè)的消息對(duì)應(yīng)的動(dòng)作序列__messages[type][i].call(this, events);} }1.3 消息注銷(xiāo)方法
“將訂閱者注銷(xiāo)的消息從消息隊(duì)列中清除”
remove: function(type, fn) {// 如果消息隊(duì)列存在if(__messages[type] instanceof Array){// 從最后一個(gè)消息動(dòng)作遍歷var i = __message[type].length - 1;for(; i>= 0; i--) {// 如果存在該動(dòng)作則在消息動(dòng)作序列中移除相應(yīng)動(dòng)作__messages[type][i] === fn && __messages[type].splice(i, 1);}} }2.面向?qū)ο髮?duì)象的程序設(shè)計(jì)
- 參考JavaScript高級(jí)程序設(shè)計(jì)(第三版)
面向?qū)ο?Object-Oriented, OO)的語(yǔ)言有一個(gè)標(biāo)志,那就是它們都有類(lèi)的概念,而通過(guò)類(lèi)可以創(chuàng)建任意多個(gè)具有相同屬性和方法的對(duì)象。ECMAScript中沒(méi)有類(lèi)的概念,因此它的對(duì)象也與類(lèi)的語(yǔ)言的對(duì)象有所不同。
ECMA-262把對(duì)象定義為:“無(wú)序?qū)傩缘募?其屬性可以包含基本值、對(duì)象或者函數(shù)”。嚴(yán)格來(lái)講,這就相當(dāng)于說(shuō)對(duì)象是一組沒(méi)有特定順序的值。對(duì)象的每個(gè)屬性和方法都有一個(gè)名字,而每個(gè)名字都映射到一個(gè)值。正因?yàn)檫@樣,我們可以把ECMAScript的對(duì)象想象成散列表:無(wú)非就是一組名值對(duì),其中值可以是數(shù)據(jù)或函數(shù)
2.1 理解對(duì)象
- 創(chuàng)建一個(gè)對(duì)象的最簡(jiǎn)單方式
- 創(chuàng)建一個(gè)Object的實(shí)例,然后再為它添加屬性和方法
早期的JavaScript開(kāi)發(fā)人員使用上述方法創(chuàng)建對(duì)象。幾年后,對(duì)象字面量成為創(chuàng)建這種對(duì)象的首選模式:
var person = {name: "Nicholas",age: 29,job: "Software Engineer",sayName: function(){alert(this.name);} };2.1.1 屬性類(lèi)型
ECMA-262第5版在定義只有內(nèi)部才用的特征(attribute)時(shí),描述了屬性(property)的各種特征。ECMA-262定義這些特性是為了給JavaScript引擎用的,因此在JavaScript中我們不能直接訪(fǎng)問(wèn)它們。為了表示特性是內(nèi)部值,該規(guī)范把它們放在了兩對(duì)兒方括號(hào)中,例如[ [Enumerable] ]
數(shù)據(jù)屬性
數(shù)據(jù)屬性包含一個(gè)數(shù)據(jù)值的位置。在這個(gè)位置可以讀取和寫(xiě)入值。數(shù)據(jù)屬性有4個(gè)描述其行為的特性
- [ [Configurable]]: 表示能否通過(guò)delete刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為訪(fǎng)問(wèn)器屬性。像前面例子中那樣直接在對(duì)象上定義的屬性,默認(rèn)為true。
- [ [Enumberable]]:表示能否通過(guò)for-in循環(huán)返回屬性,默認(rèn)為true。
- [ [Writable]]:表示能否修改屬性的值,默認(rèn)為true。
- [ [Value]]:屬性的數(shù)據(jù)值,默認(rèn)為undefined
對(duì)于像前面栗子中的那樣直接在對(duì)象上定義屬性,它們的[ [Configurable]]、[ [Enumberable]]和[ [Writable]]特性都被設(shè)置為true,而[ [Value]]特性被設(shè)置為指定的值。
要修改屬性默認(rèn)的特性,必須使用ECMAScript5的Object.defineProperty()方法。這個(gè)方法接收三個(gè)參數(shù):屬性所在的對(duì)象、屬性的名字和一個(gè)描述符對(duì)象。其中,描述符(descriptor)對(duì)象的屬性必須是:configurable、enumerable、writable和value。
var person = {}; Object.defineProperty(person,"name", {writable: false,value: "Nicholas" });訪(fǎng)問(wèn)器屬性不包含數(shù)據(jù)值;它們包含一對(duì)兒getter和setter函數(shù)。在讀取訪(fǎng)問(wèn)器屬性時(shí),會(huì)調(diào)用getter函數(shù),這個(gè)函數(shù)負(fù)責(zé)返回有效的值;在寫(xiě)入訪(fǎng)問(wèn)器屬性時(shí),會(huì)調(diào)用setter函數(shù)并傳入新值,這個(gè)函數(shù)負(fù)責(zé)決定如何處理數(shù)據(jù)。
- [[Configurable]]: 表示能否通過(guò)delete刪除屬性從而重新定義屬性。
- [[Enumerable]]: 表示能否通過(guò)for-in循環(huán)返回屬性。
- [[Get]]:在讀取屬性時(shí)調(diào)用的函數(shù),默認(rèn)值為undefined
- [[Set]]:在寫(xiě)入屬性時(shí)調(diào)用的函數(shù),默認(rèn)值為undefined
訪(fǎng)問(wèn)器屬性不能直接定義,必須使用Object.defineProperty()來(lái)定義。
var book = {_year: 2004,edition: 1 }; Object.defineProperty(book, "year", {get: function() {return this._year;},set: function(newValue) {if(newValue > 2004){this._year = newValue;this.edition += newValue - 2004;}} }); book.year = 2005; alert(book.edition);2.1.2 定義多個(gè)屬性
ECAMAScript5 又定義了一個(gè)Object.defineProperties()方法。利用這個(gè)方法可以通過(guò)描述符一次定義多個(gè)屬性
var book = {}; Object.defineProperties(book, {_year: {writable: true,value: 2004},edition: {writable: true,value: 1},year: {get: function() {return this._year;},set: function(newValue) {if(newValue > 2004) {this._year = newValue;this.edition += newValue - 2004;}}} });2.1.3 讀取屬性的特性
使用ECMAScript5的Object.getOwnPropertyDescriptor()方法,可以取得給定屬性的描述符。這個(gè)方法接收兩個(gè)參數(shù): 屬性所在的對(duì)象和要讀取其描述符的屬性名稱(chēng)。返回值是一個(gè)對(duì)象,如果是訪(fǎng)問(wèn)器屬性,這個(gè)對(duì)象的屬性又configurable、enumerable、get和set;如果是數(shù)據(jù)屬性,這個(gè)對(duì)象的屬性又configurable、enumerable、writable和value。
var book = {}; Object.defineProperties(book, {_year: {value: 2004},edition: {value: 1},year: {get: function(){return this._year;},set: function(newValue){if (newValue > 2004) {this._year = newValue;this.edition += newValue - 2004;}}} }); var descriptor = Object.getOwnPropertyDescriptor(book, "_year"); alert(descriptor.value); // 2004 alert(descriptor.configurable); // false alert(typeof descriptor.get); // "undefined"var descriptor = Object.getOwnPropertyDescriptor(book, "year"); alert(descriptor.value); alert(descriptor.enumerable); alert(typeof descriptor.get);2.2 工廠(chǎng)模式
- 對(duì)象字面量的缺點(diǎn): 使用同一個(gè)接口創(chuàng)建很多對(duì)象,會(huì)產(chǎn)生大量的重復(fù)代碼
- 抽象了創(chuàng)建具體對(duì)象的過(guò)程
- 考慮到ECMAScript中無(wú)法創(chuàng)建類(lèi),開(kāi)發(fā)人員發(fā)明了如下函數(shù):
- 工廠(chǎng)模式的缺點(diǎn): 沒(méi)有解決對(duì)象識(shí)別問(wèn)題,即怎樣知道一個(gè)對(duì)象的類(lèi)型.(如上面應(yīng)該可以檢測(cè)出是一個(gè)createPerson類(lèi))
2.3 構(gòu)造函數(shù)模式
- ECMAScript中的構(gòu)造函數(shù)可用來(lái)創(chuàng)建特定類(lèi)型的對(duì)象。
2.3.1 構(gòu)造函數(shù)和工廠(chǎng)模式的區(qū)別
- 沒(méi)有顯示地創(chuàng)建對(duì)象
- 直接將屬性和方法賦給了this對(duì)象
- 沒(méi)有return語(yǔ)句
2.3.2 new構(gòu)造函數(shù)
- 使用new操作符調(diào)用構(gòu)造函數(shù),實(shí)際上會(huì)經(jīng)歷以下4個(gè)步驟:
(1) 創(chuàng)建一個(gè)新對(duì)象;
(2) 將構(gòu)造函數(shù)的作用域賦給新對(duì)象(this就指向了這個(gè)新對(duì)象);
(3) 指向構(gòu)造函數(shù)中的代碼(為這個(gè)新對(duì)象添加屬性);
(4) 返回新對(duì)象
[栗子]:
創(chuàng)建自定義的構(gòu)造函數(shù)意味著將來(lái)可以將它的實(shí)例標(biāo)識(shí)為一種特定的類(lèi)型。
2.3.3 不使用new操作符調(diào)用構(gòu)造函數(shù)
function Person(name, age, job) {this.name = name;this.age = age;this.job = job;this.sayName = function(){alert(this.name);} } Person("Greg", 27, "Doctor"); alert(Window.name); // "Greg"- 將會(huì)在全局對(duì)象上掛載這些屬性和方法.
2.3.4 構(gòu)造函數(shù)的問(wèn)題
- 構(gòu)造函數(shù)的主要問(wèn)題是: “每個(gè)方法都要在每個(gè)實(shí)例上重新創(chuàng)建一遍”
- 從以上角度來(lái)看,每一個(gè)Person實(shí)例都包含一個(gè)不同的Function實(shí)例
- 更確切的講,以構(gòu)造函數(shù)創(chuàng)建的函數(shù),會(huì)導(dǎo)致不同的作用域鏈和標(biāo)識(shí)符解析
- 以下代碼可以證明函數(shù)的方法是不同的.
3.小結(jié)
3.1 工廠(chǎng)模式
- 抽象了對(duì)象的創(chuàng)建過(guò)程
- 缺點(diǎn)是: 使用工廠(chǎng)模式創(chuàng)建的對(duì)象是無(wú)法識(shí)別為某一個(gè)類(lèi)的
3.2 構(gòu)造函數(shù)模式
-
解決了工廠(chǎng)模式類(lèi)的識(shí)別問(wèn)題.
-
缺點(diǎn)是: 不同實(shí)例之間相同的方法是不同的.浪費(fèi)了資源.
-
解決辦法: 原型模式
總結(jié)
以上是生活随笔為你收集整理的JavaScript --- [学习笔记]观察者模式 理解对象 工厂模式 构造函数模式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C#中string.Concat方法的使
- 下一篇: JavaScript --- [学习笔