1、定義數組
無序屬性的集合。數據結構
說白了就是一個容器,能夠容納【基本值、對象或者函數】,這些東西都叫作屬性。每一個屬性都有一個名字,每一個名字都映射一個值(能夠是基本類型的值,也能夠是引用類型的值)。從以上描述上來看很像Java裏面的Map,但形似神不似,它有它的特色。app
2、對象的屬性特色函數
一、 屬性類型this
a) 數據屬性prototype
數據屬性包含一個數據值得位置,在這個位置能夠讀取和寫入值,這類屬性有四個描述符描述其特性設計
① 、Configurable:顧名思義是否可配置(經過delete刪除屬性,可否該變其爲訪問器屬性),默認true。指針
② 、Enumerable:表示可否經過for-in循環返回屬性,默認true對象
③ 、Writable:可否修改屬性的值,默認true繼承
④ 、Value:包含這個屬性的數據值。讀取屬性的時候,從這個位置讀,寫入時把新值保存在這個位置。默認undefined。
以上能夠理解爲數據屬性的數據結構,能夠經過Object.defineProperty()方法來修改屬性默認的特性,具體方法參照API。
b) 訪問器屬性
訪問器屬性不包含數據值,他們包含一對兒getter(讀)和setter(寫)函數(非必須)。特性也有4個,前兩個跟數據屬性一致,另外兩個是
① 、Get:讀取屬性時調用的函數。默認undefined
② 、Set:寫入屬性時調用的函數。默認undefined
訪問器屬性只能經過Object.defineProperty()來定義,它長用於訪問一些只能經過訪問器屬性訪問的屬性,一般這類屬性如下劃線’_’開頭。
二、 Object.defineProperties()方法能夠同時定義多個屬性的特性。
三、 Object.getOwnPropertyDescriptor()方法,能夠取得給定屬性的描述符。
經過以上介紹,實現一個遍歷對象屬性以及屬性特性描述符的功能就沒什麼問題了。
3、建立對象的方法
一、 簡單方法
a) 構造函數定義後再追個追加屬性var person = new Object();person.name=’xxx’;
b) 對象字面量定義 var person = {name:’xxxx’;};
優勢:直觀
缺點:若是我要建立不少相同的對象,會產生大量的重複代碼。
二、 工廠模式
爲解決1中的缺點產生的一種建立對象的方法
Function createPerson(name){
Var o = new Object();
o.name=name;
return o;
}
Var person1 = createPerson(‘xxxxx’);
Var person2 = createPerson(‘xxxxxxxxxx’);
這種模式解決了重複代碼的問題,其實還有問題沒有解決掉:實際應用中拿到變量極可能須要判斷這個變量是哪種變量的類型,而後再進行不一樣的處理邏輯。注意這裏:變量的類型不要單純的理解爲ECMASript規定的6幾種變量類型,要把它理解的更廣義一點,向上面的代碼其實就是想建立一種person的數據類型。
三、 構造函數模式
理解函數與構造函數的區別,不要想太多,直接從字面上去理解,構造函數也是一種函數,它本質上也是Object類型。當表達式中用【new 構造函數名(參數)】這種形式來使用這個函數時,他會返回一個新的對象,這個對象中的屬性就是函數中定義的各類屬性的鏡像(除了prototype)。
Function Person(name){
This.name=name;
This.helloWold= function(){console.log(this.name)};
}
Var person1 = new Person(‘xxx’);
Var person2 = new Person(‘111’);
一般構造函數名首字母大寫以區別於普通的函數。
Person一、person2分別保存着Person的一個不一樣的實例,那麼問題來了person1與person2是怎麼和Person關聯的呢?其實person1,person2中有一個constructor屬性,是一個引用類型指向Person。
缺點:經過構造函數建立的對象都是不一樣的對象,這些對象中包含了代碼相同函數,也就是說在每個對象中都定義了邏輯相同的方法。這彷佛有點冗餘的趕腳。
四、 原型模式
a) 原型對象的特色
每個函數都有一個prototype(原型)屬性,它是一個引用類型,指向一個對象,這個對象的的特色是:包含了一些屬性和方法,這些屬性和方法屬於某個類型的全部而且能夠爲類型的全部實例共用。是否是很像Java中類的靜態變量,靜態方法的意思啊?
Function Person(){};
Person.prototype.name=’xxxx’;
Person.prototype.hellworld= function(){console.log(this.name)};
Var p1 = new Person();
Var p2 = new Person();
Alert(p1.name);alert(p2.name);
神奇的發現經過prototype全部Person的實例共享了prototype上的屬性。
在默認狀況下全部原型對象都會自動得到一個constructor屬性,這個屬性包含一個執行prototype屬性所在函數的指針。當調用構造函數建立一個實例後,該實例內部也會有一個prototype屬性,該屬性指向構造函數的原型對象。機:Person.prototype==person1.prototype==person2.prototype。
能夠經過isPrototypeOf()方法來肯定對象之間是否存在這種關係。
ECMAScript5增長了一個新方法:Objec.getPrototypeOf()能夠取得一個對象的原型。
HasOwnProperty()方法能夠檢測一個屬性是存在於實例中仍是存在於原型中,這個方法只在給定屬性存在於實例中時,纔會返回true。這個方法與in操做符有區別,in操做符是隻要可以經過實例訪問到給定的屬性時都返回ture,不管屬性實在實例中仍是在原型中。
For in與Object.keys()均可以取得對象上全部可枚舉的屬性,前者包含了實例對象+原型對象上的屬性,後者僅僅是實例對象屬性。
Object.getOwnPropertyNames()方法能夠獲得對象的全部屬性,不管屬性是否可枚舉。
b) 經過對象字面量設置對象的prototype。
經過對象字面量的方法設置prototype(本質上是重寫默認的prototype對象)會切斷prototype與對象之間的聯繫,即:constructor屬性再也不指向對象了。可是能夠經過顯示指定的方式創建這種鏈接(即在對象字面量中定義一個constructor屬性指向對象)。
c) 缺點:
原型中全部的屬性都是被不少實例共享的,這種共享對於函數比較合適,對於那些只包含基本值得屬性也OK,然而對於包含引用類型(好比數組)的屬性來講,某一個實例修改了這個原型屬性,全部的實例都會受到影響。這恐怕在大多數應用中都是不想看到的,由於,實例通常都是要有屬於本身的所有屬性。
五、 組合使用構造函數和原型模式
這種模式解決了上面所說的問題,經過構造函數模式定義實例屬性,而原型模式用於定義方法和共享屬性。這樣每一個實例都會有本身的一份實例屬性的副本,同時又共享着對方法的引用,最大限度地節省了內存。
六、 動態原型模式
這種方式只是寫法上與5不一樣,它將prototype的定義寫在了構造函數內部,完成了對prototype的內部封裝。
七、 寄生構造函數模式
這實際上是一種適配器模式,在一個構造函數內部再次封裝了對象。
八、 穩妥構造函數模式
所謂穩妥構造指構造函數內部沒有公共的屬性,並且其方法也不引用this的對象。與寄生構造模式類型,使用穩妥構造函數模式建立的對象與構造函數之間也沒有什麼關係,使用instanceof操做符對這種對象也沒有什麼意義。
4、對象的繼承
繼承是OO程序設計中一個基本的概念,通常經過接口繼承和實現繼承,接口繼承只繼承方法簽名,而實現繼承則繼承實際的方法。
ECMAScript中沒有方法簽名(沒有返回值類型,參數統一用arguments表示),因此只支持實現繼承,依靠原型鏈來實現。
一、 原型鏈
a) 概念:
構造函數與實例之間的關係是,每一個實例都有一個屬性prototype指向構造函數的prototype對象。
若是構造函數的prototype對象指向另外一個類型的實例對象,此時會發生什麼現象?
① 構造函數的prototype對象與構造函數之間切斷聯繫;
② 構造函數的prototype對象包含了一個指向另外一個類型的原型對象
經過這種prototype的指向咱們能夠一直延伸下去,這樣就構成了一條原型鏈條,這就是原型鏈的概念。
b) 缺點:
① 、在經過原型來實現繼承時,原型實際上會變成另外一個類型的實例,因而,原先的實例屬性也就變成了如今的原型屬性了。這樣新建立的全部實例可能會共享一些不該該共享的屬性。與原型模式建立對象的問題同樣。
② 在建立對象的實例時,沒有辦法再不影響全部對象實例的前提下向父對象的構造函數傳遞參數
二、 Constructor stealing
爲解決上述問題,使用的一種技術手段,即在子對象的構造函數內部調用父對象的構造函數(經過使用apply()和call()方法,函數只不過是在特定環境中執行代碼的對象,這個認識很強大)
Function SuperType(){this.colors=[‘aa’,’bb’,’cc’]};
Function SubType(){SuperType.call(this)};
Var instance1 = new SubType();
Instance1.colors.push(‘dd’);
Console.log(instance1.colors);// ‘aa’,’bb’,’cc’,’dd’
Var instance2 = new SubType();
Console.log(instance2.colors);// ‘aa’,’bb’,’cc’
可是如何解決構造函數帶來的代碼重複的問題?目前父對象原型中定義的方法對子對象是不可見的。
三、 構造函數+原型鏈模式(combination inheritance)
很天然的就想到經過這種方式來解決問題,經過原型鏈來實現對原型屬性和方法的繼承,經過借用構造函數來實現對實例屬性的繼承。
問題:子對象實例和原型上都存在父對象的實例屬性,實例方法。
四、 寄生組合式繼承
3中出現問題的緣由是執行了兩次父對象的構造函數,即:子對象構造函數中調用一次,子對象prototype賦值又一次,兩次重複部分就是父對象實例屬性與實例方法。
緣由清除了,解決起來就不是很難了,在子對象原型賦值的時候賦一個父對象原型的實例就能夠了。
Function inheritPrototype(subType, superType){
Var prototype = Object(superType.prototype);//建立父對象原型的實例
Prototype.constructor = subType;//構造器指向子對象
SubType.prototype=prototype;//制定子對象原型指向包裝後的父對象原型
}
這段代碼是否是很優雅啊~~~
本文屬於我的讀書總結,轉載請註明出處