類式繼承(構造函數)
JS中實際上是沒有類的概念的,所謂的類也是模擬出來的。特別是當咱們是用new 關鍵字的時候,就使得「類」的概念就越像其餘語言中的類了。類式繼承是在函數對象內調用父類的構造函數,使得自身得到父類的方法和屬性。call和apply方法爲類式繼承提供了支持。經過改變this的做用環境,使得子類自己具備父類的各類屬性。
var father = function() {
this.age = 52;
this.say = function() {
alert('hello i am '+ this.name ' and i am '+this.age + 'years old');
}
}
var child = function() {
this.name = 'bill';
father.call(this);
}
var man = new child();
man.say();
原型繼承
繼承不在對象自己,而在對象的原型上(prototype)。
每個對象都有原型,在瀏覽器中它體如今一個隱藏的__proto__屬性上。在一些現代瀏覽器中你能夠更改它們。
當一個對象須要調用某個方法時,它會去最近的原型上查找該方法,若是沒有找到,它會再次這樣逐級查找,一直找到了要找的方法。 這些查找的原型構成了該對象的原型鏈。原型最後指向的是null。
原型繼承,就是將父對像的方法給子類的原型。子類的構造函數中不擁有這些方法和屬性。
var father = function() {
}
father.prototype.a = function() {
}
var child = function(){}
//開始繼承
child.prototype = new father();
var man = new child();
man.a();
能夠看到第七行實現了原型繼承。不少人並不陌生這種方式。經過在瀏覽器中打印man咱們就能夠查看各個原型的繼承關係。
能夠看到逐級的關係child->object(father實例化的對象)->father。child是經過中間層繼承了father的原型上的東西的。可是爲何中間還有一層object呢,爲何不把child.prototype = father.prototype。答案是若是這樣作child和father就沒有區別了。你們應該還記得在prototype中有個constructor屬性,指向的是構造函數。按照正常的狀況咱們要把constructor的值改回來指向child的構造函數。但若是直接把father.prototype賦值給child.prototype,那麼constructor應該指向誰呢?因此很顯然只能經過中間層才能使得child和father保持爲獨立的對象。
對比
和原型對比起來,構造函數(類)式繼承有什麼不同呢?首先,構造函數繼承的方法都會存在父對象之中,每一次實例,都回將funciton保存在內存中,這樣的作法毫無覺得會帶來性能上的問題。其次類式繼承是不可變的。在運行時,沒法修改或者添加新的方法,這種方式是一種固步自封的死方法。而原型繼承是能夠經過改變原型連接而對子類進行修改的。另外就是類式繼承不支持多重繼承,而對於原型繼承來講,你只須要寫好extend對對象進行擴展便可。
組合模式
另外的一種模式,是結合類繼承和原型繼承的各自優勢來進行對父類的繼承。用類式繼承屬性,而原型繼承方法。這種模式避免了屬性的公用,由於通常來講,每個子類的屬性都是私有的,而方法獲得了統一。這種模式稱爲組合模式,也是繼承類式經常使用到的一種方法。
function father() {
this.a = 'father'
}
father.prototype.b = function() {
alert(this.a)
}
var child = function() {
father.call(this)
}
child.prototype = new father();
new 關鍵字和Obeject.create方法
在文章中,博主指出了使用new關鍵字的弊端。他說:「new關鍵字掩蓋了Javascript中真正的原型繼承,使得它更像是基於類的繼承。其實new關鍵字只是Javascript在爲了得到流行度而加入與Java相似的語法時期留下來的一個殘留物」。做者推薦咱們使用Object.create方法建立或者實例化對象。露珠作過測試,使用new和使用object.create方法都是將對象添加到原型上去。咱們能夠看一下代碼:
var father = function() {
this.a = 'father'
}
father.prototype.b = function() {alert(this.a)}
var obj = new father();
在瀏覽器中打印obj,能夠觀察它的結構。它自己是一個對象,有自身屬性,同時在其__proto__熟悉上也有b方法。在__proto__的後面有father,能夠看出原型是自father來的。
那麼objcet.create方法呢,咱們也能夠經過下面代碼測試之:
var father = {
a: 'father',
b: function() {
alert(this.a);
}
}
var obj = Object.create(father);
console.dir(obj)
下面是瀏覽器輸出的結果:
能夠看到,用create的方法構造出來的對象,a屬性和b方法都是在對象的原型上,也就是說咱們能夠經過更改father的屬性動態改變obj的原型上的方法和屬性,而上面經過new關鍵字用構造函數生成的實例,a屬性是沒法改變的。從這裏,咱們也能夠看到類繼承和原型基礎的一些區別。
結論
原型繼承比較符合js這種語言的特色。由於它自己就是js強大的原型的一部分。而類式繼承,與其稱它爲繼承方式,毋寧說是一種函數的運用技巧來模擬繼承罷了。本文是滷煮的一己之見,錯誤偏頗在所不免,若是有之,請各位斧正。