1.工廠模式javascript
function createPerson(name,age,job){ var o=new Object(); o.name=name; o.age=age; o.job=job; o.sayName=function(){ alert(this.name); };
return o; } var person1=createPerson("Nicholas",29,"Software Engineer"); var person2=createPerson("Greg",27,"Doctor");
優勢:解決了建立多個類似對象的問題。java
缺點:沒有解決對象識別問題(即怎樣知道一個對象的類型)安全
2.構造函數模式函數
function Person(name,age,job){ this.name=name; othis.age=age; this.job=job; this.sayName=function(){ alert(this.name); }; } var person1=new Person("Nicholas",29,"Software Engineer"); var person2=new Person("Greg",27,"Doctor");
與工廠模式相比,這裏沒有顯式地建立對象;直接將屬性和方法賦給了this對象;沒有return語句。按照慣例,構造函數始終都應該以一個大寫字母開頭,而非構造函數應該以一個小寫字母開頭。在這個例子中,person1和person2分別保存着Person的一個不一樣實例,這兩個對象都有一個constructor(構造函數)屬性。任何函數,只要經過new操做來調用,那它就能夠做爲構造函數;而任何函數,若是不經過new操做符來調用,那它和普通函數就沒什麼兩樣。this
優勢:建立自定義的構造函數意味着未來能夠將它的實例標識爲一種特定的類型;而這正是構造函數模式賽過工廠模式的地方。spa
缺點:每一個方法都要在每一個實例上從新建立一遍。prototype
改進:指針
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; this.sayName=sayName; } function sayName(){ alert(this.name); }; var person1=new Person("Nicholas",29,"Software Engineer"); var person2=new Person("Greg",27,"Doctor");
將sayName()函數的定義轉移到了構造函數外部。而構造函數內部,咱們將sayName屬性設置成等於全局的sayName函數。這樣一來,因爲sayName包含的是一個指向函數的指針,所以person1和person2對象就共享了在全局做用域中定義的同一個sayName()函數。但是若是對象須要定義不少方法,那麼就要定義不少全局函數,因而咱們這個自定義的引用類型就絲毫沒有封裝性可言。code
3.原型模式對象
function Person(){
} Person.prototype.name="Nicholas"; Person.prototype.age=29; Person.prototype.job="Software Engineer"; Person.prototype.sayName=function(){ alert(this.name); }; var person1=new Person(); person1.sayName(); var person2=new Person(); person2.sayName();
alert(person1.sayName==person2.sayName); //true
咱們建立的每個函數都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含能夠由特定類型的全部實例共享的屬性和方法。不過要明確的真正重要的一點就是,這個鏈接存在於實例與構造函數的原型對象之間,而不是存在於實例與構造函數之間。
每當代碼讀取某個對象的某個屬性時,都會執行一次搜索,目標是具備給定名字的屬性。搜索首先從對象實例自己開始。若是在實例中找到了具備給定名字的屬性,則返回該屬性的值;若是沒有找到,則繼續搜索指針指向的原型對象,在原型對象中查找具備給定名字的屬性。
function Person(){ } Person.prototype.name="Nicholas"; Person.prototype.age=29; Person.prototype.job="Software Engineer"; Person.prototype.sayName=function(){ alert(this.name); }; var person1=new Person(); var person2=new Person(); person1.name="Greg"; alert(person1.name);//"Greg"——來自實例 alert(person2.name);//"Nicholas"——來自原型
使用hasOwnProperty()方法能夠檢測一個屬性是存在於實例中,仍是存在於原型中。它是從object繼承來的,只在給定屬性存在於對象實例中時,纔會返回TRUE。上面例子中person1重寫了name屬性,故調用person1.hasOwnProperty("name");時纔會返回true;
你們應該注意到了,前面例子中每添加一個屬性和方法就要敲一遍Person.prototype,爲減小沒必要要的輸入,也爲了從視覺上更好地封裝原型的功能,更常見的方法是用一個包含全部屬性和方法的對象字面量來重寫整個原型對象。
function Person(){ } Person.prototype={ name:"Nicholas", age:29, job:"software engineer", sayName:function(){ alert(this.name); } };
可是上面的方法會使constructor屬性再也不指向Person了,爲此能夠在Person.prototype添加一句,constructor:Person,以確保經過該屬性能夠訪問到適當的值。
優勢:可讓全部對象實例共享它所包含的實例和方法。換句話說,沒必要在構造函數中定義對象實例的信息,而是能夠將這些信息直接添加到原型對象中。
缺點:它省略了爲構造函數傳遞初始化參數這一環節,結果全部實例在默認狀況下都將取得相同的屬性值。這種共享對於函數很是合適,對於那些包含基本值的屬性也說得過去,但對於包含引用類型值的屬性來講,問題就比較突出了。以下:
function Person(){ } Person.prototype={ constructor:Person, name:"Nicholas", age:29, job:"Software Engineer", friend:["one","two"], sayName:function(){ alert(this.name); } }; var person1=new Person(); var person2=new Person(); person1.friend.push("three"); alert(person1.friend); //"one,two,three" alert(person2.friend); //"one,two,three" alert(person1.friend===person2.friend);//true
這也是不多看到有人單獨使用原型模式的緣由所在。
4.組合使用構造函數模式和原型模式
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; this.friends=["one","two"]; } Person.prototype={ constructor:Person, sayName:function(){ alert(this.name); } } var person1=new Person("Nicholas",29,"softwarre Engineer"); var person2=new Person("Greg",27,"Doctor"); person1.friends.push("three"); alert(person1.friends); //"one,two,three" alert(person2.friends); //"one,two" alert(person1.friends==person2.friends); //false alert(person1.sayName==person2.sayName); //true
這種組合模式中構造函數模式用於定義實例屬性,而原型模式用於定義方法和共享的屬性。這種組合模式是在ECMAScript中使用最普遍,認同度最高的一種建立自定義類型的方法。
5.動態原型模式
function Person(name,age,job){ this.name=name; this.age=age; this.job=job; if(typeof this.sayName !="function"){ Person.prototype.sayName=function(){ alert(this.name); } } } var friend=new Person("Nicholas",29,"Software Engineer"); friend.sayName();
這裏只在sayName()方法不存在的狀況下才會將它添加到原型中。這段代碼只有在初次調用的時候纔會執行。
6.寄生構造函數模式
function Person(name,age,job){ var o=new Object(); o.name=name; o.age=age; o.job=job; o.sayName=function(){ alert(this.name); }; return o; } var friend=new Person("Nicholas",29,"Software Engineer"); friend.sayName();
一般在前面幾種模式都不適用的狀況下,可使用寄生構造函數模式。這種模式的基本思想是建立一個函數,該函數的做用僅僅是封裝建立對象的代碼,而後再返回新建立的對象。關於寄生構造函數模式,有一點要說明:首先,返回的對象與構造函數或者與構造函數的原型屬性之間沒有關係;也就是說,構造函數的返回的對象與在構造函數外部建立的對象沒有什麼不一樣。爲此不能依賴instanceof操做符來肯定對象類型。
7.穩妥構造函數模式
function Person(name,age,job){ var o=new Object(); //能夠在這裏定義私有變量和函數 o.sayName=function(){ alert(name); }; return o; } var friend=Person("Nicholas",29,"Software Engineer"); friend.sayName();
Douglas Crockford發明了javascript中的穩妥對象這個概念。所謂穩妥對象,指的是沒有公共屬性,並且其方法也不引用this的對象。穩妥對象最適合在一些安全的環境中(這些環境會禁止使用this和new)時使用。穩妥構造函數遵循與寄生構造函數相似的模式,但有兩點不一樣:一是新建立對象的實例不引用this;二是不使用new操做符調用構造函數。在上面的例子中,除了sayName()方法外,沒有別的方式能夠訪問其數據成員。