標籤:
之前學習js僅僅是把w3school上的基本文法看了一次而已,再後來細看書的時候,書中會出現很多很多沒有聽過的文法,其中一個就是js的繼承以及總能看到的prototype。我主要在看的兩本js書是《javascript權威指南》,也就是那本犀牛書,還有一本是瘋狂xx系列的《瘋HTML5/CSS3/Javascript講義》。前者非常適合用啃js細節,如果需要深入學習一些js內部機制以及相關的內容,這本大塊頭啃起來還是挺有味道的。後者是本速成教材,可以讓你對某個概念有一個非常舒服的開啟檔案,不晦澀,淺顯易懂。大家如果看完w3school之後,感覺沒有學到什麼東西的話,應該試著去看看書,這是我剛開始學習前端總結出來的小建議。
先說說繼承,在js裡面其實是沒有繼承的概念。但大家應該都知道,在物件導向的程式設計裡,類之間有顯示的繼承關係,一個類可以顯示地指定繼承自哪一個類,然後繼承下來的子類就會有父類的屬性和方法。js裡沒有這一概念,但是它一種類似繼承的方法來對原來的類進行擴充。這種方式就是通過類的prototype屬性,來為類動態增加屬性和方法,這種方式類似於繼承,但它只能稱作是“偽繼承”,因為這種繼承的實質是修改了原有的類,而不是產生一個子類。
我們先說不用繼承的時候,js通過什麼樣的方法來為對象增加屬性和方法呢。
首先,程式為一個對象的並不存在屬性進行賦值的時候,就可以認為是為這個對象增加了屬性。
【代碼01】
var obj = {}; obj.name = "Jeremy"; obj.info = function(){ console.log("This is a method"); }
在這裡插入說明一下函數、方法、對象還有類的概念理解。
函數,在javascript裡面有“一等公民”之稱,當在js中定義了一個函數之後,其實就是產生了以下:
1)函數:類似於java的方法,它可以被調用;
2)對象:當定義了一個函數時候,系統也就建立了一個對象,這個對象是Function類的執行個體;
3)方法:定義了一個函數的時候,這個函數會被附加給某個對象,作為某個對象的方法:
【代碼02】
function Person(name,gender){var val = "valueName";//局部變數this.name = name;this.gender = gender;//定義了一個匿名函數。相當於為Person對象指定了info方法this.info = function(){console.log("name:"+this.name);console.log("gender:"+this.gender);console.log("val:"+val);}}var p1 = new Person(‘Jeremy‘,‘male‘);//執行個體p1去調用方法info()var v = p1.info();console.log("這裡能訪問到局部變數val:"+v);
4)類:在定義函數的同時,即得到一個和函數同名的類,而這個函數就是該類的構造器,所以在定義一個函數的時候,實際
就是定義了一個構造器。
我們來看上面的代碼02。
程式定義了一個函數Person,同時也就定義了Person類,函數即類的構造器,這個函數為Person執行個體p1提供了info方法。
雖然這樣我們也完成了對Person的方法增加,但是這樣做會存在一些問題。例如效能會低下,每次建立Person對象的時候就會有很多info函數,這樣會造成系統記憶體流失(實際是什麼情況我暫時沒有去探究了),引起效能下降。還有一個很明顯的問題,知道js閉包的朋友應該發現了,在info()函數裡訪問局部變數val,形成了閉包(這裡不對閉包展開說了)。然而這會導致val變數的範圍擴大,在代碼末尾,程式依舊可以訪問到局部變數值。
所以!為了避免這些不好的事情的發生!建議使用prototype屬性。
首先在javascript中,每一個javascript對象(null除外)都會和另外一個對象相關聯,這個另一個對象就是原型,js中的每一個對象都是從原型那裡繼承屬性。在建立對象的3種方法裡面,通過對象直接量建立的對象都具有同一個對象原型,可以通過Object.prototype獲得對對象原型的引用。通過關鍵字new和建構函式建立的對象的原型即使建構函式的prototype的值。所以,和使用var empty={};建立對象一樣,通過new Object()建立的對象也繼承自Object.prototype。
【對象直接量建立對象】即由若干的名/值對組成的映射表。就像這樣:
var empty={};//屬性為空白的對象var person={name:"gua",age:"18"};//具有name和age屬性的值
【new關鍵字建立對象】通過new關鍵字建立並初始化一個新的對象。形式上就是new後面跟一個函數調用,這裡的函數稱作建構函式(constructor),這個
建構函式用於初始化一個新建立的對象。就像這樣:
var obj = new Object();var arr = new Array();
換個好理解一些的說法,就是js對象都是有相同基類(object)的執行個體。
嗯,我也覺得這一段比較晦澀。
那來看一下代碼是怎麼實現的就好了。
function Person(name,gender){this.name = name;this.gender = gender;//定義了一個匿名函數。相當於為Person對象指定了info方法this.info = function(){console.log("name:"+this.name);console.log("gender:"+this.gender);}}var p1 = new Person(‘Jeremy‘,‘male‘);p1.info();//為Person類的prototype屬性增加了walk函數,即可以認為為Person類動態增加walk執行個體方法Person.prototype.walk = function(){console.log(this.name +" 正在散步.......一步一步.....");}var p2 = new Person(‘Guagua‘,‘female‘);p2.info();//執行p2的walk方法p2.walk(); //js已經為類Person動態增加了方法walk,所以p1也具有了walk方法。p1.walk();
運行結果:
這段程式採用prototype為Person類增加了一個walk方法,這讓所有的Person執行個體共用了一個walk方法,而walk方法並不在Person函數之內,所以就不會產生閉包。
所以我們應該避免使用內嵌函數為類定義方法,使用增加prototype屬性的方式來增加方法。
以上是我最近看書學習整理出來的一些內容,有不對的地方望大家指正。
Javascript中的繼承與Prototype