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