現(xiàn)在有一個"動物"對象的構(gòu)造函數(shù)掸鹅。
function Animal(){
this.species = "動物";
}
還有一個"貓"對象的構(gòu)造函數(shù)。
function Cat(name,color){
this.name = name;
this.color = color;
}
怎樣才能使"貓"繼承"動物"呢亡鼠?
一旭从、 構(gòu)造函數(shù)綁定
使用call或apply方法稳强,將父對象的構(gòu)造函數(shù)綁定在子對象上,即在子對象構(gòu)造函數(shù)中加一行:
function Cat(name,color){
Animal.apply(this, arguments);
this.name = name;
this.color = color;
}
var cat1 = new Cat("大毛","黃色");
alert(cat1.species); // 動物
二和悦、 prototype模式
如果"貓"的prototype對象退疫,指向一個Animal的實例,那么所有"貓"的實例鸽素,就能繼承Animal了褒繁。
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
var cat1 = new Cat("大毛","黃色");
alert(cat1.species); // 動物
上面,我們將Cat的prototype對象指向一個Animal的實例馍忽。
Cat.prototype = new Animal();
它相當(dāng)于完全刪除了prototype 對象原先的值,然后賦予一個新值。
Cat.prototype.constructor = Cat;
任何一個prototype對象都有一個constructor屬性絮爷,指向它的構(gòu)造函數(shù)壶硅。如果沒有"Cat.prototype = new Animal();"這一行,Cat.prototype.constructor是指向Cat的瓦呼;加了這一行以后喂窟,Cat.prototype.constructor指向Animal。
alert(Cat.prototype.constructor == Animal); //true
另外央串,每個實例也有一個constructor屬性磨澡,默認調(diào)用prototype對象的constructor屬性。
alert(cat1.constructor == Cat.prototype.constructor); // true
因此质和,在運行"Cat.prototype = new Animal();"這一行之后稳摄,cat1.constructor也指向Animal。
alert(cat1.constructor == Animal); // true
這顯然會導(dǎo)致繼承鏈的紊亂(cat1明明是用構(gòu)造函數(shù)Cat生成的)饲宿,因此需要手動糾正厦酬,將Cat.prototype對象的constructor值改為Cat(即上面代碼第二行)。
如果替換了prototype對象瘫想,
o.prototype = {};
那么弃锐,下一步是為新的prototype對象加上constructor屬性,并將這個屬性指回原來的構(gòu)造函數(shù)殿托。
o.prototype.constructor = o;
三霹菊、 直接繼承prototype
由于Animal對象中,不變的屬性都可以直接寫入Animal.prototype支竹。所以旋廷,我們可以讓Cat()跳過 Animal(),直接繼承Animal.prototype礼搁。
先將Animal對象改寫:
function Animal(){ }
Animal.prototype.species = "動物";
然后饶碘,將Cat的prototype對象,指向Animal的prototype對象馒吴,這樣就完成了繼承扎运。
Cat.prototype = Animal.prototype;
Cat.prototype.constructor = Cat;
var cat1 = new Cat("大毛","黃色");
alert(cat1.species); // 動物
與前一種方法相比瑟曲,這樣做效率更高(不用執(zhí)行和建立Animal的實例),而且更省內(nèi)存豪治。但Cat.prototype和Animal.prototype現(xiàn)在指向了同一個對象洞拨,那么任何對Cat.prototype的修改,都會反映到Animal.prototype负拟。
所以烦衣,上面代碼第二行其實是有問題的:
Cat.prototype.constructor = Cat;
這里實際上把Animal.prototype對象的constructor屬性也改掉了!
alert(Animal.prototype.constructor); // Cat
四掩浙、 利用空對象作為中介
由于"直接繼承prototype"存在上述的缺點花吟,所以就有第四種方法,利用一個空對象作為中介厨姚。
var F = function(){};
F.prototype = Animal.prototype;
Cat.prototype = new F();
Cat.prototype.constructor = Cat;
F是空對象衅澈,所以幾乎不占內(nèi)存。這時谬墙,修改Cat的prototype對象矾麻,就不會影響到Animal的prototype對象。
alert(Animal.prototype.constructor); // Animal
我們將上面的方法芭梯,封裝成一個函數(shù)险耀,便于使用。
function extend(Child, Parent) {
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.uber = Parent.prototype;
}
使用的時候玖喘,方法如下
extend(Cat,Animal);
var cat1 = new Cat("大毛","黃色");
alert(cat1.species); // 動物
這個extend函數(shù)甩牺,就是YUI庫如何實現(xiàn)繼承的方法。
另外累奈,在函數(shù)體最后一行
Child.uber = Parent.prototype;
意思是為子對象設(shè)一個uber屬性贬派,這個屬性直接指向父對象的prototype屬性。這相當(dāng)于在子對象上打開一條通道澎媒,可直接調(diào)用父對象的方法搞乏。這一行放在這里,只是為了實現(xiàn)繼承的完備性戒努,純屬備用性質(zhì)请敦。
五、 拷貝繼承
如果把父對象的所有屬性和方法储玫,拷貝進子對象侍筛,也能夠?qū)崿F(xiàn)繼承。
首先撒穷,把Animal的所有不變屬性匣椰,都放到它的prototype對象上。
function Animal(){}
Animal.prototype.species = "動物";
然后端礼,再寫一個函數(shù)禽笑,實現(xiàn)屬性拷貝的目的入录。
function extend2(Child, Parent) {
var p = Parent.prototype;
var c = Child.prototype;
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
}
這個函數(shù)的作用,就是將父對象的prototype對象中的屬性佳镜,全部拷貝給Child對象的prototype對象僚稿。
使用時這樣寫:
extend2(Cat, Animal);
var cat1 = new Cat("大毛","黃色");
alert(cat1.species); // 動物
至于非構(gòu)造函數(shù)的繼承,請參考http://www.reibang.com/p/fde29e3f7116