js中建立對象的方式有不少,尤爲是基於原型的方式建立對象,是理解基於原型繼承的基礎。所以在這裏彙總一下,並對各類方法的利弊進行總結和對比,不至於之後對這些概念有模糊。javascript
var o = new Object();
咱們都知道,實際上在javascript中並無所謂的類的概念。所以在建立對象時不能像面嚮對象語言那樣,經過類A new出來一個a對象。可是javascript有一個特殊的Object。咱們能夠藉助Object來new一個對象。
而後能夠經過以下方式給對象增長屬性或方法:java
o.name="abc";
可是由於沒有類的約束,這種方式建立出來的對象沒法實現對象的重複利用,而且沒有一種固定的約數,操做起來可能會出現這樣或者那樣的意想不到的問題。
有這樣一個例子:json
var a = new Object; var b = new Object; var c = new Object; c[a]=a; c[b]=b; alert(c[a]===a); //輸出什麼
關於這個例子的具體解答詳見 例子出處函數
直接看一個例子:優化
function createPerson(){ var o = new Object(); o.name = "abc"; o.age = 20; return o; }
這種建立對象的方法是在一個function中new Object(),而且賦予屬性和方法,最後return 帶有這些屬性和方法的對象。this
可是當咱們想經過spa
var p1 = createPerson(); alert(typeof(p1));//Object 僅能獲得這個結果,實際上沒有太大意義 alert(p1 instanceof(類名???))//會發現其實並不存在一個Person類
function Person(name,age){ this.name = name; this.age = age; this.say = function(){ // } } var p1 = new Person('abc',20);
構造函數的方式建立的對象:prototype
函數名即爲類名code
經過this來定義屬性對象
經過new Person()建立對象
而且有以下屬性:
alert(typeof(p1));// Person alert(p1 instanceof(Person));// true
可是同時咱們也發現這種方式建立對象的一個弊端。對於類中的say方法,每分配一個對象就會有一個say的內存空間被分配出來。有一個say的拷貝。若是方法特別多的時候,會形成內存空間的極大浪費。能夠經過兩種方式進行優化和改進:
將say聲明爲全局的
function say(){ }
而後在類的定義中經過:
this.say = say;
採用下面提到的基於原型的方式建立對象
讓類中的行爲統一指向全局的say方法。可是若是將全部的方法設爲全局的時候,就能夠被window對象調用,那麼就破壞了對象的封裝性;若是方法不少,會形成代碼中充斥着大量的全局函數。
function Person(){ } Person.prototype.name = "abc"; Person.prototype.age= 20; Person.prototype.say= function(){ alert(this.name+this.age); } var p1 = new Person(); p1.say();//ok say();//no 完成了封裝
原型是js中的一個特殊對象。當一個函數建立以後,會隨之產生一個原型對象。該函數中的prototype屬性指向該源性對象;
當經過該函數的構造函數建立一個具體對象時,在這個對象中,就會有一個_prop_屬性指向原型。這些是js中的很重要的一種繼承方式--基於原型的繼承的基礎。這裏再也不贅述。
基於原型建立對象時,若是對象的屬性和方法特別多時,能夠經過以下方式進行定義:
Person.prototype = { name:"abc", age:20, say:function(){ } }
稱爲原型重寫。原型重寫以後當咱們再經過 var p1 = new Person()建立一個對象時,p1 的constructor != Person()了。因爲原型重寫了,並且沒有經過prototype指向,從而指向了Object()。
若是constructor比較重要,能夠再json格式的定義中手動制定
constructor:Pserson
關於原型重寫,我畫了個示意圖,比較容易理解:
p1是原型重寫前聲明的對象,p2是原型重寫
Person.prototype.name = "123";
以後的聲明的對象。
能夠看出
constructor的指向確實沒有自動變換,除非經過上述手動的方式進行修改。
經過p2.name = "456",設置name時,會在本身的存儲空間中存儲。固然查找name屬性時,也是從本身的內存空間中讀取name值。