轉(zhuǎn)自:360圖書館 ??時間:2016-9-26
原標(biāo)題:使用Javascript,可以實現(xiàn)多層繼承
1.javascript的繼承1:多重繼承
有3個原型夷陋,分別處理學(xué)生的姓名欠拾,性別和年齡(當(dāng)然是比較復(fù)雜的處理)胰锌,而我們需要實例化的就是一個學(xué)生的原型,于是多重繼承誕生了藐窄,我們看代碼:
var s1 = function(name){
this.name = name;
}
var s2 = function(sex){
this.sex = sex;
}
var s3 = function(age){
this.age = age;
}
var Student = function(name, sex, age, score){
s1.call(this, name);
s2.call(this, sex);
s3.call(this, age);
this.score = score;
}
Student.prototype.constructor = Student;
var s = new Student('jack', 'male', '12', '100');
alert(s.name);//jack
alert(s.sex);//male
alert(s.age);//12
alert(s.score);//100
這樣我們就可以根據(jù)各個不同的功能模塊分不同程序員獨立開發(fā)资昧,最后合并起來,實現(xiàn)了多重繼承荆忍。
當(dāng)然如果參數(shù)過多的話可以使用object參數(shù)來做格带。而且各個功能模塊耦合度比較小,出現(xiàn)BUG也能很快定義到功能模塊刹枉,修改其中某一個對其他沒有影響叽唱。
多重繼承小記
此法實現(xiàn)多重繼承,是利用call/apply實現(xiàn)多重繼承嘶卧,核心就是用Function類的call/apply方法去綁定新的類尔觉,使新的類實例化后的對象繼承了該屬性及方法
/*-------利用call/apply實現(xiàn)多重繼承--------*/
//基類1
function Base1(){
this.name? = "base1";
this.getName = function(){
alert(this.name);
}
};
//基類2
function Base2(){
this.act = "eating";
this._item = "banana";
this.saying = function(){
alert("I'm "+this.act+" the "+this._item+"!");
};
};
//利用call/apply多重繼承
function Extend(){
//arguments為傳進的參數(shù)偽數(shù)組
for(var i=0; i
arguments[i].call(this);
}
}
//實例化一個對象,并繼承自Base1,Base2
var myClass = new Extend(Base1,Base2);
myClass.getName();
myClass.saying();
復(fù)制代碼
但它的缺點是基類的方法只能定義在類中,這樣在每次實例化的時候都會創(chuàng)建該方法芥吟,造成多余內(nèi)存占用
2侦铜、javascript的繼承2:原型鏈多層繼承
原型鏈就是利用prototype屬性來實現(xiàn)的,例如:原型1.prototype = new 原型2() ;
這樣就可以利用實例化1 = new 原型1()钟鸵;
實例化1就可以完全訪問所有的原型1和原型2的方法钉稍,這里要注意在實例化時不要向原型函數(shù)傳入?yún)?shù),參數(shù)需要在實例化以后再定義棺耍。
var s1 = function(){
}
s1.prototype.getName = function(){alert(this.name)};
var s2 = function(){
}
s2.prototype =new s1();
s2.prototype.constructor = s2;
s2.prototype.getSex = function(){alert(this.sex)};
var s3 = function(){
}
s3.prototype = new s2();
s3.prototype.constructor = s3;
s3.prototype.getAge = function(){alert(this.age)};
var s = new s3();
s.name = 'jack';
s.sex = 'male';
s.age = '22';
s.getName();//jack
s.getSex();//male
s.getAge();//22
這樣通過prototype也實現(xiàn)了多層繼承贡未,不過個人感覺沒有上面多重繼承來的直觀。不過最好的辦法是2者齊上陣蒙袍。
針對多余內(nèi)存被占用俊卤,所以第二種方法是用prototype原型的形式來完成多重繼承
/*-------利用prototype實現(xiàn)多重繼承--------*/
//基類1
function Base1(){
this.name = "Base1";
//動態(tài)原型模式
if(typeof this.getName !== "function"){
Base1.prototype.getName = function(){
alert(this.name);
};
}
}
//基類2
function Base2(){
this.act = "eating";
this._item = "banana";
//動態(tài)原型模式
if(typeof this.getName !== "function"){
Base2.prototype.saying=function(){
alert("I'm "+this.act+" the "+this._item+"!");
};
}
};
//利用prototype多重繼承;
function extend(){
var Class = function(){};
for(var i=0; i
var base = new arguments[i]();
for(var j in base){
Class.prototype[j] = base[j];
}
};
return Class;
}
//創(chuàng)建一個類并繼承base1,base2類
var MyClass = extend(Base1,Base2);
//實例化一個對象
var myClass = new MyClass();
myClass.getName();
myClass.saying();
復(fù)制代碼
不過,用prototype繼承還有缺點害幅,就是無法傳遞參數(shù)
3.javascript的繼承3:混合模式繼承
我們可以做如下2個實驗消恍,以下2個代碼塊充分說明了以上2種模式的優(yōu)劣,其實代碼塊2已經(jīng)是混合模式了以现。
代碼塊1:var s1 = function(name){
this.name = name;
this.getName = function(){
alert(this.name);
}
}
var o1 = new s1('jack');
var o2 = new s1('marry');
alert(o1.getName === o2.getName);//false;
代碼塊2:var s1 = function(name){
this.name = name;
}
s1.prototype.getName = function(){
alert(this.name);
}
var o1 = new s1('jack');
var o2 = new s1('marry');
alert(o1.getName === o2.getName);//true;
我們來分析一下結(jié)果狠怨,第一種情況,將s1的getName方法存放在實例中邑遏,這樣以后每實例化一個新對象都將生成不同的getName()方法佣赖,而如果將getName()方法添加到s1原型的原型鏈中后(也有人稱之為反射,借鑒與JAVA)记盒,每實例化一個新的對象調(diào)用的都還是同一個getName()方法憎蛤,這樣正是我們所需要的,可以更少的占用內(nèi)存資源提高運行效率纪吮。
如果全部使用prototype方式來進行繼承的話又不能在實例化的同時傳參俩檬,所以兩者混合模式是最常用的方式栏豺,我們看代碼:
var s1 = function(name){
this.name = name;
}
s1.prototype.getName = function(){alert(this.name)};
var s2 = function(sex){
this.sex = sex;
}
s2.prototype =new s1();
s2.prototype.getSex = function(){alert(this.sex)};
var s3 = function(age){
this.age = age
}
s3.prototype = new s2();
s3.prototype.getAge = function(){alert(this.age)};
var s4 = function(name, sex, age){
s1.call(this, name);
s2.call(this, sex);
s3.call(this, age);
}
s4.prototype = new s3();
s4.prototype.constructor = s4;
var s = new s4('jack', 'male', '25');
s.getName();//jack
s.getSex();//male
s.getAge();//25
這樣3重常用的繼承方法介紹完畢了,混合模式適合一些比較大型javascript開發(fā)豆胸,頻繁的實例化原型,可以提高運行效率巷疼。
使用了兩種混合的方法晚胡,擁有各自的優(yōu)點
/*-------混合型實現(xiàn)多重繼承--------*/
//基類1
function Base1(name){
this.name = name;
//動態(tài)原型擴展
if(typeof this.getName !== "function"){
Base1.prototype.getName = function(){
alert(this.name);
};
}
}
//基類2
function Base2(act,_item){
this.act = act;
this._item = _item;
//動態(tài)原型擴展
if(typeof this.saying!== "function"){
Base2.prototype.saying=function(){
alert("I'm "+this.act+" the "+this._item+"!");
};
}
};
//原型多重繼承
function prototypeExtend(){
for(var i=0; i
var base = new arguments[i]();
for(var j in base){
this.prototype[j] = base[j];
}
}
}
//定義一個類,內(nèi)部用call/apply繼承
function MyClass(name,act,_item){
Base1.call(this,name);
Base2.apply(this,[act,_item]);
};
//多重繼承類的原型方法
prototypeExtend.apply(MyClass,[Base1,Base2]);
//實例化
var my = new MyClass("Der","eating","apple");
my.saying();
my.getName();
復(fù)制代碼
上面的方法構(gòu)造出來的實例的屬性都無法實現(xiàn)私有嚼沿,另一種函數(shù)化返回對象來實現(xiàn)繼承模式估盘,則可以很好的利用閉包在函數(shù)內(nèi)部實現(xiàn)私有變量。
核心代碼如下:
//基類
var Der = function(o){
//私有變量
var name = o.name;
var age = o.age;
var action = function(){
age++;
};
//創(chuàng)建一個對象,可訪問內(nèi)部私有變量
var that = {
saying:function(){
action();
return "I'm "+name+",i am "+age+" years old!";
}
};
//返回對象
return that;
};
//子類
var Panda = function(o){
//創(chuàng)建一個對象繼承自Der
var that = Der(o);
//私有變量(差異化設(shè)置)
var fullName = o.fullName;
that.getFullName = function(){
return fullName;
};
//返回一個對象
return that;
};
//實例化
var der = Der({"name":"der","age":26});
document.writeln(der.saying()+"
");
var panda = Panda({"name":"panda","fullName":"zhengPanda","age":25});
document.writeln(panda.saying()+"
");
document.writeln(panda.getFullName()+"
");
復(fù)制代碼
運行代碼:
函數(shù)化對象繼承(帶私有變量)
//基類
var Der = function(o){
//私有變量
var name = o.name;
var age = o.age;
var action = function(){
age++;
};
//創(chuàng)建一個對象,可訪問內(nèi)部私有變量
var that = {
saying:function(){
action();
return "I'm "+name+",i am "+age+" years old!";
}
};
//返回對象
return that;
};
//子類
var Panda = function(o){
//創(chuàng)建一個對象繼承自Der
var that = Der(o);
//私有變量(差異化設(shè)置)
var fullName = o.fullName;
that.getFullName = function(){
return "my fullname is "+fullName;
};
//返回一個對象
return that;
};
//實例化
var der = Der({"name":"der","age":26});
document.writeln(der.saying()+"
");
var panda = Panda({"name":"panda","fullName":"zhengPanda","age":25});
document.writeln(panda.saying()+"
");
document.writeln(panda.getFullName()+"
");
復(fù)制代碼
補充一下:
對于一些私有方法骡尽,比如只想給類調(diào)用不想給實例調(diào)用的方法可以寫在 function 內(nèi)部:
例如:var s1 = function(){
var a = function(){
...
}
this.b = function(){}
}
var x = new s1();
var y = new s1();
alert(x.b === y.b) //false這樣y和x都無法直接調(diào)用a這個函數(shù)遣妥。但是這樣會造成內(nèi)存浪費。每實例化一個 s1,函數(shù)a和this.b方法都會占用額外內(nèi)存攀细,所以我們可以這樣改寫:
var s1 =function(){
function a(){
...
}
return function(){
this.b = function(){}
}
}
var x = new s1();
var y = new s1();
alert(x.b === y.b) //true;
于是箫踩,這樣a方法可以拿到實例化時的參數(shù)和運行環(huán)境,而b方法并沒有因為多次實例化而浪費內(nèi)存谭贪。