雖然Object構造函數或對象字面量均可以用來建立單個對象,可是這些方法有明顯的缺點:使用同一個接口建立不少對象,會產生大量重複代碼。所以人們開始使用工廠模式。數組
這種模式抽象了建立具體對象的過程安全
function createPerson(name,age){ var o = new Object(); o.name = name; o.age = age; o.sayName = function(){ alert(this.name); }; return o; } var person1 = new createPerson("liaojin",18); var person2 = new createPerson("xiaoguan",20);
函數createPerson()可以根據接受的參數來構建一個包含全部必要信息的Person對象。能夠無數次的調用這個函數,而每次它都會返回一個包含兩個屬性以個方法的對象。工廠模式雖然解決了建立多個類似對象的問題,但卻沒有解決對象識別的問題(即怎樣知道一個對象的類型)。app
使用構造函數將上述例子重寫函數
function Person(name,age){ this.name = name; this.age = age; this.sayName = function(){ alert(this.name); }; } var person1 = new Person("liaojin",18); var person2 = new Person("xiaoguan",20);
在這個例子中,Person()函數取代了createPerson()函數。注意到Person()中的代碼與createPerson()的不一樣之處:
1.沒有顯示的建立對象
2.直接將屬性和方法賦值給了this對象
3.沒有return語句
要建立Person新實例,必須使用new操做符。這種方式會經理如下四個步驟:
1.建立一個新對象
2.將構造函數的做用域賦給新對象(所以this就指向了這個新對象)
3.執行構造函數中的代碼(爲這個新對象添加屬性)
4.返回新對象
person1和person2分別保存着Person的一個不一樣實例。這兩個對象都有一個constructor屬性,該屬性指向Person。this
alert(person1.constructor == Person);//true alert(person2.constructor == Person);//true
對象的constructor屬性最初是用來表示對象類型的,可是檢測對象類型仍是instanceof更可靠一些。這個例子中建立的全部對象便是Object的實例,同時也是Person的實例。
建立自定義的構造函數意味着未來能夠將它的實例標識爲一種特定的類型;person1和person2之因此同時是Object的實例,是由於全部對象均繼承自Object.prototype
將構造函數當作函數
構造函數與其餘函數惟一的區別就在於調用他們的方式不一樣。
任何函數只要經過new操做符來調用,那他就能夠做爲構造函數;而任何函數若是不經過new操做符來調用,那他跟普通函數沒有區別。
如上述例子中的Person()函數能夠經過下列任何一種方式來調用。指針
//當作構造函數調用 var person = new Person("liaojin",18); person.sayName();//liaojin //做爲普通函數調用 Person("lihua",12); window.sayName();//lihua //在另外一個對象的做用域調用 var o = new Object(); Person.call(o,"xiaoguan",20); o.sayName();//xiaoguan
構造函數的問題
構造函數模式雖然好用,可是也有缺點。使用構造函數的主要問題,就是每一個方法都要在每一個實例上從新建立一遍。如同this.sayName =new function(){alert(this.name);};在上面的構造函數中sayName()的方法,person1和person2雖然都調用了這個方法,可是調用的並非同一個Function實例。所以不一樣實例的同名函數是不相等的code
alert(person1.sayName == person2.sayName);//false
然而建立兩個完成一樣任務的Function實例的確沒有必要;所以能夠經過吧函數定義轉移到構造函數外部來解決這個問題對象
function Person(name,age){ this.name = name; this.age = age; this.sayName = sayName; } function sayName(){ alert(this.name); } var person1 = new Person("liaojin",18); var person2 = new Person("xiaoguan",20);
在構造函數內部,咱們將sayName屬性設置爲指向全局的sayName函數,因爲sayName包含的是指向函數的指針,person1,person2共享了一個sayName函數,解決了兩個函數作一樣一件事的問題。
但是隨即有產生了新的問題:在全局做用域定義的函數實際上只能被某個對象調用,這讓全局函數優勢名存實亡。若是對象須要定義不少方法,那麼就須要定義多個全局函數,因而自定義的引用類型就沒有封裝性可言了,所以產生了原型模式。繼承
咱們所建立的每一個函數都有一個prototype屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途就是包含能夠由特定類型的全部實例共享的屬性和方法。prototype就是經過調用構造函數而建立的那個對象實例的原型對象。可讓全部對象的實例共享它所包含的屬性和方法。
function Person(){} Person.prototype.name = "liaojin"; Person.prototype.age = 18; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); Person1.sayName();//liaojin var person2 = new Person(); Person2.sayName();//liaojin alert(person1.sayName == person2.sayName);//true
構造函數用於定義實例屬性,而原型模式用於定義方法和共享的屬性。這樣每一個實例都會有本身的實例屬性的副本,但同時又共享着對方法的引用,最大限度地節省了內存。
function Person(name,age){ this.name = name; this.age = age; this.friends = ["lucy","lily"]; } Person.prototype = { constructor:Person, sayName:function(){ alert(this.name); } } var person1 = new Person("liaojin",18); var person2 = new Person("xiaoguan",20); person1.friends.push("lihua"); alert(person1.friends);//lucy,lily,lihua alert(person2.friends);//lucy,lily alert(person1.friends == person2.friends);//false alert(person1.sayName == person2.sayName);//true
有其餘面向對象經驗開發的人員看到獨立的構造函數和原型時,極可能會肥腸困惑,動態原型模式就是解決這個問題的一個方案。
function Person(name,age){ this.name = name; this.age = age; } if(typeof this.sayName != "function"){ Person.prototype.sayName = function(){ alert(this.name); }; }
這裏只在sayName()方法不存在的狀況下才會將它添加到原型中。這段代碼只會在初期調用纔會執行。
這個模式能夠在特殊的狀況下用來爲對象建立構造函數。假設咱們想建立一個具備額外方法的特殊數組。因爲不能直接修改Array構造函數,所以可使用這個模式。
function SpecialArray(){ var values = new Array(); values.push.apply(values,arguments); values.toPipedString = function(){ return this.join("|"); }; return values; } var colors = new SpecialArray["red","blue","green"]; alert(colors.toPipedString());//red|blue|green
說明:關於寄生構造函數模式,首先返回的對象與構造函數或者與構造函數的原型屬性之間沒有關係;構造函數返回的對象與在構造函數外部建立的對象沒有什麼不一樣。所以不能依賴instanceof操做符來肯定對象的類型。
所謂穩妥對象,指的是沒有公共屬性,並且其方法也不引用this的對象。穩妥對象最適合在一些安全的環境中(禁止使用this和new的環境),或者在防止數據被其餘應用程序改動時使用。與寄生構造函數相似的模式;但有兩點不一樣:1.新建立對象的實例方法不引用this;2.不使用new操做符調用構造函數;
function Person(name,age){ var o = new Object(); o.sayName = function(){ alert(name); }; return o; } //使用 var friend = Person("liaojin",18); friend.sayName();//liaojin
這樣變量person中保存的是一個穩妥對象,而除了調用sayName()方法外,沒有別的方法能夠訪問其數據成員,即便有其餘代碼會給這個對象添加方法或數據成員,但也不可能有別的辦法訪問傳入到構造函數中的原始數據,很是適合在某些安全執行環境下使用。
與寄生構造函數模式相似,使用穩妥構造函數模式建立的對象與構造函數之間也沒有什麼關係,所以instanceof操做符對這種對象也沒有什麼意義