前段時間曾經寫過一篇關於prototype原型的文章(http://www.cnblogs.com/ttcc/p/3751604.html),可是對於JS的核心之一,仍是應該多多熟悉才行,常回過頭來看看總有新味道。下面的內容我的很清晰,但並非出自本人之手(原文連接本文最後),借花獻佛收藏了:html
用過JavaScript的同窗們確定都對prototype如雷貫耳,可是這到底是個什麼東西卻讓初學者莫衷一是,只知道函數都會有一個prototype屬性,能夠爲其添加函數供實例訪問,其它的就不清楚了,最近看了一些 JavaScript高級程序設計,終於揭開了其神祕面紗。編程
每一個函數都有一個prototype屬性,這個屬性是指向一個對象的引用,這個對象稱爲原型對象,原型對象包含函數實例共享的方法和屬性,也就是說將函數用做構造函數調用(使用new操做符調用)的時候,新建立的對象會從原型對象上繼承屬性和方法。數組
在具體說prototype前說幾個相關的東東,能夠更好的理解prototype的設計意圖。以前寫的一篇JavaScript 命名空間博客提到過JavaScript的函數做用域,在函數內定義的變量和函數若是不對外提供接口,那麼外部將沒法訪問到,也就是變爲私有變量和私有函數。瀏覽器
1 function Obj(){ 2 var a=0; //私有變量
3 var fn=function(){ //私有函數
4
5 } 6 }
這樣在函數對象Obj外部沒法訪問變量a和函數fn,它們就變成私有的,只能在Obj內部使用,即便是函數Obj的實例仍然沒法訪問這些變量和函數函數
1 var o=new Obj(); 2 console.log(o.a); //undefined
3 console.log(o.fn); //undefined
當定義一個函數後經過 「.」爲其添加的屬性和函數,經過對象自己仍然能夠訪問獲得,可是其實例卻訪問不到,這樣的變量和函數分別被稱爲靜態變量和靜態函數,用過Java、C#的同窗很好理解靜態的含義。測試
1 function Obj(){ 2
3 } 4
5 Obj.a=0; //靜態變量
6
7 Obj.fn=function(){ //靜態函數
8
9 } 10
11 console.log(Obj.a); //0
12 console.log(typeof Obj.fn); //function
13
14 var o=new Obj(); 15 console.log(o.a); //undefined
16 console.log(typeof o.fn); //undefined
在面向對象編程中除了一些庫函數咱們仍是但願在對象定義的時候同時定義一些屬性和方法,實例化後能夠訪問,JavaScript也能作到這樣this
1 function Obj(){ 2 this.a=[]; //實例變量
3 this.fn=function(){ //實例方法
4
5 } 6 } 7
8 console.log(typeof Obj.a); //undefined
9 console.log(typeof Obj.fn); //undefined
10
11 var o=new Obj(); 12 console.log(typeof o.a); //object
13 console.log(typeof o.fn); //function
這樣能夠達到上述目的,然而spa
1 function Obj(){ 2 this.a=[]; //實例變量
3 this.fn=function(){ //實例方法
4
5 } 6 } 7
8 var o1=new Obj(); 9 o1.a.push(1); 10 o1.fn={}; 11 console.log(o1.a); //[1]
12 console.log(typeof o1.fn); //object
13 var o2=new Obj(); 14 console.log(o2.a); //[]
15 console.log(typeof o2.fn); //function
上面的代碼運行結果徹底符合預期,但同時也說明一個問題,在o1中修改了a和fn,而在o2中沒有改變,因爲數組和函數都是對象,是引用類型,這就說明o1中的屬性和方法與o2中的屬性與方法雖然同名但卻不是一個引用,而是對Obj對象定義的屬性和方法的一個複製。prototype
這個對屬性來講沒有什麼問題,可是對於方法來講問題就很大了,由於方法都是在作徹底同樣的功能,可是卻又兩份複製,若是一個函數對象有上千和實例方法,那麼它的每一個實例都要保持一份上千個方法的複製,這顯然是不科學的,這可腫麼辦呢,prototype應運而生。設計
不管何時,只要建立了一個新函數,就會根據一組特定的規則爲該函數建立一個prototype屬性,默認狀況下prototype屬性會默認得到一個constructor(構造函數)屬性,這個屬性是一個指向prototype屬性所在函數的指針,有些繞了啊,寫代碼、上圖!
function Person(){
}
根據上圖能夠看出Person對象會自動得到prototyp屬性,而prototype也是一個對象,會自動得到一個constructor屬性,該屬性正是指向Person對象。
當調用構造函數建立一個實例的時候,實例內部將包含一個內部指針(不少瀏覽器這個指針名字爲__proto__)指向構造函數的prototype,這個鏈接存在於實例和構造函數的prototype之間,而不是實例與構造函數之間。
1 function Person(name){ 2 this.name=name; 3 } 4
5 Person.prototype.printName=function(){ 6 alert(this.name); 7 } 8
9 var person1=new Person('Byron'); 10 var person2=new Person('Frank');
Person的實例person1中包含了name屬性,同時自動生成一個__proto__屬性,該屬性指向Person的prototype,能夠訪問到prototype內定義的printName方法,大概就是這個樣子的
寫段程序測試一下看看prototype內屬性、方法是可以共享:
1 function Person(name){ 2 this.name=name; 3 } 4
5 Person.prototype.share=[]; 6
7 Person.prototype.printName=function(){ 8 alert(this.name); 9 } 10
11 var person1=new Person('Byron'); 12 var person2=new Person('Frank'); 13
14 person1.share.push(1); 15 person2.share.push(2); 16 console.log(person2.share); //[1,2]
果不其然!實際上當代碼讀取某個對象的某個屬性的時候,都會執行一遍搜索,目標是具備給定名字的屬性,搜索首先從對象實例開始,若是在實例中找到該屬性則返回,若是沒有則查找prototype,若是仍是沒有找到則繼續遞歸prototype的prototype對象,直到找到爲止,若是遞歸到object仍然沒有則返回錯誤。一樣道理若是在實例中定義如prototype同名的屬性或函數,則會覆蓋prototype的屬性或函數。
1 function Person(name){ 2 this.name=name; 3 } 4
5 Person.prototype.share=[]; 6
7 var person=new Person('Byron'); 8 person.share=0; 9
10 console.log(person.share); //0而不是prototype中的[]
固然prototype不是專門爲解決上面問題而定義的,可是卻解決了上面問題。瞭解了這些知識就能夠構建一個科學些的、複用率高的對象,若是但願實例對象的屬性或函數則定義到prototype中,若是但願每一個實例單獨擁有的屬性或方法則定義到this中,能夠經過構造函數傳遞實例化參數。
1 function Person(name){ 2 this.name=name; 3 } 4
5 Person.prototype.share=[]; 6
7 Person.prototype.printName=function(){ 8 alert(this.name); 9 }