標籤:
一、前言
java、C#等正統物件導向語言都會提供類似extend之類的處理類的繼承的方法,而javascript並沒有提供專門的方法用於繼承,在javascript中使用繼承需要一點技巧。js中執行個體的屬性和行為是由建構函式和原型兩部分組成的,js的繼承也分為這兩部分。下面給大家分享一下在js中如何?繼承,講的不對的地方望大家指正!
二、繼承建構函式中的屬性和行為
我們定義兩個類Animal和Bird類,來實現js中類的繼承。
//定義Animal類 function Animal(name){ this.name = name; this.type = ‘animal‘; } //Animal原型添加say方法 Animal.prototype.say = function(){ alert("I‘m a(an) "+ this.type +", my name is "+ this.name); };
接下來定義Bird類
//定義Bird類function Bird(name){ this.name = name; this.type = ‘bird‘;}Bird.prototype = {};
我們先讓Bird類繼承Animal的建構函式中的屬性和行為,代碼清單如下
//定義Animal類function Animal(name){ this.name = name; this.type = ‘animal‘;}//Animal原型添加say方法Animal.prototype = { say : function(){ alert("I‘m a(an) "+ this.type +", my name is "+ this.name); }};//定義Bird類function Bird(name){ this.name = name; this.type = ‘animal‘;}Bird.prototype = {};//執行個體化Bird類var canary = new Bird(‘yinwu‘);alert(canary.type);
程式運行正常,但看不出繼承,因為在Bird類的建構函式中是將Animal的屬性和行為複製了一份。同時,缺點也很明顯,如果Animal類的建構函式有任何改動,Bird類也要手動的同步改動,同樣一份代碼,複製在程式中不同地方,違反了DRY原則,降低了代碼的可維護性。需要改進,改進後的代碼清單如下:
//定義Animal類function Animal(name){ this.name = name; this.type = ‘animal‘;}//Animal原型添加say方法Animal.prototype = { say : function(){ alert("I‘m a(an) "+ this.type +", my name is "+ this.name); }};//定義Bird類function Bird(name){ Animal.call(this, name);}Bird.prototype = {};//執行個體化Bird類var canary = new Bird(‘yinwu‘);alert(canary.type);//animal
我們把Bird類改為了Animal.call(this, name);,也就是通過call()方法實現讓Animal內部的this指向Bird類的執行個體。改進後的代碼就實現了建構函式的屬性和行為的繼承,最後alert(canary.type)彈出的資訊為animal。
三、原型的繼承
由繼承我們知道,繼承後子類擁有了父類同樣的屬性和行為,那麼能否將Animal類的原型直接傳給Bird類的原型呢?請看如下代碼清單:
//定義Animal類function Animal(name){ this.name = name; this.type = ‘animal‘;}//Animal原型添加say方法Animal.prototype = { say : function(){ alert("I‘m a(an) "+ this.type +", my name is "+ this.name); }};//定義Bird類function Bird(name){ Animal.call(this, name);}Bird.prototype = Animal.prototype;//執行個體化Bird類var canary = new Bird(‘yinwu‘);canary.say();//I‘m a(an) animal, my name is yinwu
通過把Animal類的原型直接傳給Bird類的原型,Bird類擁有了Animal原型中的say行為。如果給Bird類的原型添加fly行為,結果會如何呢?如代碼清單:
//定義Animal類function Animal(name){ this.name = name; this.type = ‘animal‘;}//Animal原型添加say方法Animal.prototype = { say : function(){ alert("I‘m a(an) "+ this.type +", my name is "+ this.name); }};//定義Bird類function Bird(name){ Animal.call(this, name);}Bird.prototype = Animal.prototype;//Bird類原型添加fly方法Bird.prototype.fly = function(){ alert("I‘m flying more high ");}//執行個體化Bird類var canary = new Bird(‘yinwu‘);canary.say();//I‘m a(an) animal, my name is yinwucanary.fly();//I‘m flying more highvar pig = new Animal(‘xiaozhu‘);pig.fly();//I‘m flying more high
程式運行你會發現Animal類也獲得了fly行為,pig對象調用fly行為彈出I‘m flying more high的提示資訊,我們只給Bird類添加fly行為,為什麼Animal類也獲得了fly行為呢?原因在於”Bird.prototype = Animal.prototype;”這句代碼,因為prototype原型本質上是hash對象,賦值時會進行傳址,也就是說Bird.prototype原型和Animal.prototype原型指向同一記憶體位址,對Bird.prototype原型添加fly行為也就相當於為Animal.prototype原型添加了fly行為,這就是pig會fly的原因。如何對代碼進行改進呢?請看如下代碼清單:
//定義Animal類function Animal(name){ this.name = name; this.type = ‘animal‘;}//Animal原型添加say方法Animal.prototype = { say : function(){ alert("I‘m a(an) "+ this.type +", my name is "+ this.name); }};//定義Bird類function Bird(name){ Animal.call(this, name);}Bird.prototype = new Animal();Bird.prototype.constructor = Bird;//Bird類原型添加fly方法Bird.prototype.fly = function(){ alert("I‘m flying more high ");}//執行個體化Bird類var canary = new Bird(‘yinwu‘);canary.say();//I‘m a(an) animal, my name is yinwucanary.fly();//I‘m flying more highvar pig = new Animal(‘xiaozhu‘);pig.fly();//報錯
與前一代碼清單相比,把”Bird.prototype = Animal.prototype;”這句代碼,改成了”Bird.prototype = new Animal(); Bird.prototype.constructor = Bird;”,這兩句什麼意思呢? ”Bird.prototype = new Animal();”意思是Bird.prototype原型作為Animal類的執行個體,那麼Bird原型對象中包含了一個指向Animal原型對象的指標;“Bird.prototype.constructor = Bird;”意思是因為“Bird.prototype = new Animal();”時,Bird.prototype.constructor指向了Animal原型對象,將其糾正重新指向Bird。代碼運行後你會發現pig.fly()會報錯,此fly()行為undefined,那是因為Animal原型中沒有fly行為。到此我們就實現了Bird類對Animal類的原型繼承。
四、js繼承的封裝
前面二、三小節中分別實現了建構函式和原型的屬性和行為的繼承,這節中我們將js中實現繼承的過程進行封裝,向外提供介面調用。請看下列代碼:
//js繼承封裝成函數並提供介面function extend(subClass, superClass){ var F = function(){}; F.prototype = superClass.prototype;//F.prototype 原型對象和superClass.prototype原型對象指向同一記憶體位址 subClass.prototype = new F();//subClass.prototype作為F類的執行個體 subClass.prototype.constructor = subClass;//將subClass.prototype.constructor重新指向subClass subClass.superclass = superClass.prototype;//subClass類中定義superclass屬性使其指向superClass.prototype if(superClass.prototype.constructor == Object.prototype.constructor){ superClass.prototype.constructor = superClass; }}//定義Animal類function Animal(name){ this.name = name; this.type = ‘animal‘;}//Animal原型添加say方法Animal.prototype.say = function(){ alert("I‘m a(an) "+ this.type +", my name is "+ this.name);};//定義Bird類function Bird(name){ this.constructor.superclass.constructor.apply(this, arguments);//通過apply方法繼承Animal類中的屬性和行為 this.type = ‘bird‘;}//實現對Animal繼承extend(Bird, Animal);Bird.prototype.fly = function(){ alert("I‘m flying more high ");};//執行個體化var canary = new Bird(‘yinwu‘);canary.say();//I‘m a(an) animal, my name is yinwucanary.fly();//I‘m flying more high
通過上述方法將js中的繼承封裝在函數中,要用到繼承時直接調用即可。
此文章發布在本人部落格園changjianqiu,如需轉載本文,請註明來源: http://www.cnblogs.com/changjianqiu/
淺談js中繼承的理解和實現