1、使用Object構造函數或對象字面量建立對象安全
var person = new Object()
person.name = 'adhehe';
person.age = 23;
//...
//或
var person = {
name:'adhehe',
age:23,
//...
}
缺點:使用同一個接口建立不少的對象,會產生大量的重複代碼。函數
2、工廠模式this
這種模式抽象了建立具體對象的過程。用函數來封裝以特定接口建立對象的細節。spa
function createPerson(name,age){
//將建立的具體細節封裝起來,最後返回一個對象 var o = new Object(); o.name = name; o.age = age; o.getName = function(){ return this.name; } return o; } var person1 = createPerson('adhehe',23); var person2 = createPerson('Jhone',45);
缺點:雖然解決了建立多個類似對象的問題,但沒有解決 對象識別的問題,即怎麼知道一個對象的類型prototype
3、構造函數模式指針
構造函數能夠用來建立特定類型的對象。如Object、Array等。也能夠建立自定義構造函數。code
function Person(name,age){ this.name = name; this.age = age; this.getName = function(){ return this.name; } } var person1 = new Person('adhehe',23); var person2 = new Person('Jhone',45);
以new關鍵字建立Person的實例,經歷如下4個步驟:對象
instanceof 操做符 能夠檢測對象的類型blog
//以person爲例 console.log(person1 instanceof Person) console.log(person2 instanceof Person) console.log(person1 instanceof Object) console.log(person2 instanceof Object) //都輸出 true
構造函數與普通函數的惟一區別是:調用的方式不一樣。只要經過new操做符調用,它就是構造函數。接口
缺點:每一個方法都要在每一個實例上從新建立一遍。
//以上面 person爲例 person1.getName == preson2.getName //false
4、原型模式
建立的每一個函數都有一個prototype(原型)屬性,它是一個指針,批向一個對象,而這個對象的用途就是:包含能夠共享的屬性和方法,給那些特定類型的全部實例。
function Person(){} Person.prototype.name = 'adhehe' Person.prototype.age = 23 Person.prototype.getName = function(){ return this.name; } var preson1 = new Person() var preson2 = new Person() console.log(person1.getName()) console.log(person2.getName()) //都返回 adhehe person1.getName == person2.getName //true
一、理解原型對象
只要建立了函數,就會爲該函數建立一個prototype屬性,這個屬性指向函數的原型對象。
全部原型對象都會自動得到一個constructor屬性(構造函數),這個屬性指向prototype屬性所在的函數的指針。
當調用構造函數建立一個實例後,實例內部將包含一個指針(__ptoto__),指向構造函數的原型對象。
Person.prototype.constructor == Person
person1.constructor == Person
person2.constructor == Person
person1.__proto__ == Person.prototype
person2.__proto__ == Person.prototype
isPrototypeOf()方法 肯定對象之間是否存在原型關係
Person.prototype.ifPrototypeOf(person1) //true Person.prototype.ifPrototypeOf(person2) //true
Object.getPrototypeOf()方法 是ECMAScript5新增的檢測方法
Object.getPrototypeOf(person1) == Person.prototype //true Object.getPrototypeOf(person2) == Person.prototype //true
每當讀取某個對象中的某個屬性時,首先搜索該對象自己,再搜索該對象指向的原型對象。若是在實例對象中某個屬性名與原型中的一個屬性同名,該屬性將會屏蔽原型中的屬性。
function Person(){} Person.prototype.same = 1; var person1 = new Person() var person2 = new Person()
person1.same = 2; console.log(person1.same) //2 console.log(person2.same) //1
使用delete操做符 能夠徹底刪除實例屬性,從而可以從新訪問原型中的屬性。
//接上例 delete person1.same; console.log(person1.same) //1
hasOwnProperty()方法能夠檢測一個屬性是存在於實例中,仍是存在於原型中。
person1.hasOwnProperty('name') // true person1.hasOwnProperty('hello') //false
二、in 操做符
有兩種方式使用:
單獨使用:經過對象可以訪問指定屬性時返回true,不管該屬性存在於實例仍是原型中。
"name" in person1 //true
同時使用 hasOwnProperty()方法和 in 操做符,就能夠確認該屬性究竟是存在於對象中,仍是存在於原型中。
function has(obj,name){ return !obj.hasOwnProperty(name) && (name in obj); } //in爲true,hasOwnProperty()返回false,就能夠肯定屬性是在原型中
for-in 循環:返回全部可以經過對象訪問的、可枚舉的屬性,其中既包括存在於實例中的屬性,也包括存在於原型中的屬性。屏蔽了原型中不可枚舉的屬性的實例屬性也會在for-in循環中返回。
Object.keys()方法 能夠取得對象上全部可枚舉的實例屬性。
function Person(){} Person.prototype.name = 'adhehe' Person.prototype.age = 23 Person.prototype.getName = function(){ return this.name; } var keys = Object.keys(Person.prototype) console.log(keys) // "name,age,getName" var person1 = new Person() person1.name = 'Rob' var person1Keys = Object.keys(person1) console.log(person1Keys) //"name"
Object.getOwnPropertyNames()方法 能夠獲得全部實例屬性,不管它是否可枚舉。
var keys = Object.getOwnPropertyNames(Person.prototype); console.log(keys) //"constructor,name,age,getName"
三、更簡單的原型語法
function Person(){} Person.prototype = { name:'adhehe', age:23, getName:function(){ return this.name } }
在這裏,constructor屬性再也不指向Person。能夠 將constructor屬性值設置回適當的值
function Person(){} Person.prototype = { constructor:Person, name:'adhehe', age:23, getName:function(){ return this.name } }
以上面這種方式重設constructor屬性會致使它的Enumerable特性被設置爲true。默認狀況下原生的constructor是不可枚舉的。可使用Object.defineProperty()修改
function Person(){} Person.prototype = { name:'adhehe', age:23, getName:function(){ return this.name } } Object.defineProperty(Person.prototype,'constructor',{ enumberable:false, value:Person })
四、原型的動態性
對原型對象的修改都可以當即 從實例上反映出來。能夠隨時爲原型添加屬性和方法。
var o = new Person(); Person.prototype.sayHi = function(){ //... } o.sayHi() //沒有問題
可是,若是重寫原型對象,則會切斷 構造函數與最初原型之間的聯繫。實例中的指針僅指向原型,而不指向構造函數。
function Person(){} var o = new Person(); Person.prototype = { constructor:Person, name:'adhehe', age:23, getName:function(){ //.... } } o.getName() // 報錯
五、原生對象的原型
全部原生的引用類型,都在其構造函數的原型上定義了方法。
咱們也能夠在原生對象的原型上定義新的方法或修改原型,但不推薦這麼作。
缺點:
function Person(){} Person.prototype = { constructor:Person, name:'adhehe', age:23, friends:['a','b'], getName:function(){ //... } } var o1 = new Person() var o2 = new Person() o1.friends.push('c') console.log(o1.friends) //a,b,c console.log(o2.friends) //a,b,c //兩個對象實例的friends返回了相同的值
5、構造函數模式 與 原型模式 組合
function Person(name,age){ this.name = name; this.age = age; this.friends = ['a','b'] } Person.prototype = { constructor:Person, getName:function(){ //... } } var o1 = new Person('adhehe',23); var o2 = new Person('Greg',45); o1.friends.push('c'); console.log(o1.friends) //a,b,c console.log(o2.friends) //a,b console.log(o1.friends === o2.friends) //false console.log(o1.getName === o2.getName) //true
6、動態原型模式
在構造函數中初始化原型(僅在必要的狀況下),它保持了同時使用構造函數和原型的優勢。
它只會在初次調用構造函數時纔會執行,此後,原型已經完成初始化,不須要再作什麼修改。
使用動態原型模式時,不能使用對象字面量重寫原型。會切斷現有實例與新原型之間的聯繫。
function Person(name,age){ this.name = name; this.age = age; if(typeof this.getName != 'function'){ Person.prototype.getName = function(){ //... } } } var o = new Person('adhehe',23) o.getName()
7、寄生構造函數模式
建立一個函數,該函數的做用僅僅是封裝建立對象的代碼,而後再返回新建立的對象。
這個模式與工廠模式是如出一轍的,不一樣的是這裏使用 new 操做符建立實例對象,並把包裝函數叫做構造函數。
function Person(name,age){ var o = new Object(); o.name = name; o.age = age; o.getName = function(){ //... } return o; } var person1 = new Person('adhehe',23) person1.getName();
缺點:返回的對象與構造函數或構造函數的原型屬性之間沒有任何關係,因此不能依賴 instanceof 操做符來肯定對象類型。在有其餘模式可用的狀況下,不推薦使用這種模式
8、穩妥構造函數模式
穩妥對象:指的是沒有公共屬性,其方法也不引用 this 的對象。
穩妥對象 最適合在一些安全的環境中,或者在防止數據被其餘應用程序改動時使用。
此模式 與 寄生構造函數相似的模式,有兩點不一樣:
function Person(name,age){ var o = new Object(); //在這裏定義私有變量與屬性 o.getName = function(){ return name; } return o; } var person1 = Person('adhehe',23) person1.getName() //adhehe //在這種模式建立的對象中,除了使用getName()方法以外,沒有其餘辦法能夠訪問name
缺點:與寄生構造函數模式相似,返回的對象與構造函數或構造函數的原型屬性之間沒有任何關係,因此不能依賴 instanceof 操做符來肯定對象類型。