轉(zhuǎn)自:360圖書(shū)館 ??時(shí)間:2016-9-26
原標(biāo)題:使用Javascript,可以實(shí)現(xiàn)多層繼承
1.javascript的繼承1:多重繼承
有3個(gè)原型,分別處理學(xué)生的姓名,性別和年齡(當(dāng)然是比較復(fù)雜的處理),而我們需要實(shí)例化的就是一個(gè)學(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ù)各個(gè)不同的功能模塊分不同程序員獨(dú)立開(kāi)發(fā),最后合并起來(lái),實(shí)現(xiàn)了多重繼承。
當(dāng)然如果參數(shù)過(guò)多的話(huà)可以使用object參數(shù)來(lái)做。而且各個(gè)功能模塊耦合度比較小,出現(xiàn)BUG也能很快定義到功能模塊,修改其中某一個(gè)對(duì)其他沒(méi)有影響。
多重繼承小記
此法實(shí)現(xiàn)多重繼承,是利用call/apply實(shí)現(xiàn)多重繼承,核心就是用Function類(lèi)的call/apply方法去綁定新的類(lèi),使新的類(lèi)實(shí)例化后的對(duì)象繼承了該屬性及方法
/*-------利用call/apply實(shí)現(xiàn)多重繼承--------*/
//基類(lèi)1
function Base1(){
this.name? = "base1";
this.getName = function(){
alert(this.name);
}
};
//基類(lèi)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為傳進(jìn)的參數(shù)偽數(shù)組
for(var i=0; i
arguments[i].call(this);
}
}
//實(shí)例化一個(gè)對(duì)象,并繼承自Base1,Base2
var myClass = new Extend(Base1,Base2);
myClass.getName();
myClass.saying();
復(fù)制代碼
但它的缺點(diǎn)是基類(lèi)的方法只能定義在類(lèi)中,這樣在每次實(shí)例化的時(shí)候都會(huì)創(chuàng)建該方法,造成多余內(nèi)存占用
2、javascript的繼承2:原型鏈多層繼承
原型鏈就是利用prototype屬性來(lái)實(shí)現(xiàn)的,例如:原型1.prototype = new 原型2() ;
這樣就可以利用實(shí)例化1 = new 原型1();
實(shí)例化1就可以完全訪問(wèn)所有的原型1和原型2的方法,這里要注意在實(shí)例化時(shí)不要向原型函數(shù)傳入?yún)?shù),參數(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
這樣通過(guò)prototype也實(shí)現(xiàn)了多層繼承,不過(guò)個(gè)人感覺(jué)沒(méi)有上面多重繼承來(lái)的直觀。不過(guò)最好的辦法是2者齊上陣。
針對(duì)多余內(nèi)存被占用,所以第二種方法是用prototype原型的形式來(lái)完成多重繼承
/*-------利用prototype實(shí)現(xiàn)多重繼承--------*/
//基類(lèi)1
function Base1(){
this.name = "Base1";
//動(dòng)態(tài)原型模式
if(typeof this.getName !== "function"){
Base1.prototype.getName = function(){
alert(this.name);
};
}
}
//基類(lèi)2
function Base2(){
this.act = "eating";
this._item = "banana";
//動(dòng)態(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)建一個(gè)類(lèi)并繼承base1,base2類(lèi)
var MyClass = extend(Base1,Base2);
//實(shí)例化一個(gè)對(duì)象
var myClass = new MyClass();
myClass.getName();
myClass.saying();
復(fù)制代碼
不過(guò),用prototype繼承還有缺點(diǎn),就是無(wú)法傳遞參數(shù)
3.javascript的繼承3:混合模式繼承
我們可以做如下2個(gè)實(shí)驗(yàn),以下2個(gè)代碼塊充分說(shuō)明了以上2種模式的優(yōu)劣,其實(shí)代碼塊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;
我們來(lái)分析一下結(jié)果,第一種情況,將s1的getName方法存放在實(shí)例中,這樣以后每實(shí)例化一個(gè)新對(duì)象都將生成不同的getName()方法,而如果將getName()方法添加到s1原型的原型鏈中后(也有人稱(chēng)之為反射,借鑒與JAVA),每實(shí)例化一個(gè)新的對(duì)象調(diào)用的都還是同一個(gè)getName()方法,這樣正是我們所需要的,可以更少的占用內(nèi)存資源提高運(yùn)行效率。
如果全部使用prototype方式來(lái)進(jìn)行繼承的話(huà)又不能在實(shí)例化的同時(shí)傳參,所以?xún)烧呋旌夏J绞亲畛S玫姆绞剑覀兛创a:
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開(kāi)發(fā),頻繁的實(shí)例化原型,可以提高運(yùn)行效率。
使用了兩種混合的方法,擁有各自的優(yōu)點(diǎn)
/*-------混合型實(shí)現(xiàn)多重繼承--------*/
//基類(lèi)1
function Base1(name){
this.name = name;
//動(dòng)態(tài)原型擴(kuò)展
if(typeof this.getName !== "function"){
Base1.prototype.getName = function(){
alert(this.name);
};
}
}
//基類(lèi)2
function Base2(act,_item){
this.act = act;
this._item = _item;
//動(dòng)態(tài)原型擴(kuò)展
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];
}
}
}
//定義一個(gè)類(lèi),內(nèi)部用call/apply繼承
function MyClass(name,act,_item){
Base1.call(this,name);
Base2.apply(this,[act,_item]);
};
//多重繼承類(lèi)的原型方法
prototypeExtend.apply(MyClass,[Base1,Base2]);
//實(shí)例化
var my = new MyClass("Der","eating","apple");
my.saying();
my.getName();
復(fù)制代碼
上面的方法構(gòu)造出來(lái)的實(shí)例的屬性都無(wú)法實(shí)現(xiàn)私有,另一種函數(shù)化返回對(duì)象來(lái)實(shí)現(xiàn)繼承模式,則可以很好的利用閉包在函數(shù)內(nèi)部實(shí)現(xiàn)私有變量。
核心代碼如下:
//基類(lèi)
var Der = function(o){
//私有變量
var name = o.name;
var age = o.age;
var action = function(){
age++;
};
//創(chuàng)建一個(gè)對(duì)象,可訪問(wèn)內(nèi)部私有變量
var that = {
saying:function(){
action();
return "I'm "+name+",i am "+age+" years old!";
}
};
//返回對(duì)象
return that;
};
//子類(lèi)
var Panda = function(o){
//創(chuàng)建一個(gè)對(duì)象繼承自Der
var that = Der(o);
//私有變量(差異化設(shè)置)
var fullName = o.fullName;
that.getFullName = function(){
return fullName;
};
//返回一個(gè)對(duì)象
return that;
};
//實(shí)例化
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ù)制代碼
運(yùn)行代碼:
函數(shù)化對(duì)象繼承(帶私有變量)
//基類(lèi)
var Der = function(o){
//私有變量
var name = o.name;
var age = o.age;
var action = function(){
age++;
};
//創(chuàng)建一個(gè)對(duì)象,可訪問(wèn)內(nèi)部私有變量
var that = {
saying:function(){
action();
return "I'm "+name+",i am "+age+" years old!";
}
};
//返回對(duì)象
return that;
};
//子類(lèi)
var Panda = function(o){
//創(chuàng)建一個(gè)對(duì)象繼承自Der
var that = Der(o);
//私有變量(差異化設(shè)置)
var fullName = o.fullName;
that.getFullName = function(){
return "my fullname is "+fullName;
};
//返回一個(gè)對(duì)象
return that;
};
//實(shí)例化
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ù)制代碼
補(bǔ)充一下:
對(duì)于一些私有方法,比如只想給類(lèi)調(diào)用不想給實(shí)例調(diào)用的方法可以寫(xiě)在 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都無(wú)法直接調(diào)用a這個(gè)函數(shù)。但是這樣會(huì)造成內(nèi)存浪費(fèi)。每實(shí)例化一個(gè) s1,函數(shù)a和this.b方法都會(huì)占用額外內(nèi)存,所以我們可以這樣改寫(xiě):
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í)例化時(shí)的參數(shù)和運(yùn)行環(huán)境,而b方法并沒(méi)有因?yàn)槎啻螌?shí)例化而浪費(fèi)內(nèi)存。