新聞中心
前段時(shí)間溫故了下JavaScript 的寫(xiě)類(lèi)方式,從這篇開(kāi)始我們看看JavaScript 的繼承方式。

創(chuàng)新互聯(lián)建站長(zhǎng)期為上1000家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開(kāi)放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為灤平企業(yè)提供專(zhuān)業(yè)的成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作,灤平網(wǎng)站改版等技術(shù)服務(wù)。擁有十多年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開(kāi)發(fā)。
面向?qū)ο蟮恼Z(yǔ)言多數(shù)都支持繼承,繼承最重要的優(yōu)點(diǎn)就是代碼復(fù)用,從而構(gòu)建大型軟件系統(tǒng)。如果一個(gè)類(lèi)能夠重用另一個(gè)類(lèi)的屬性和或方法,就稱(chēng)之為繼承。從這個(gè)角度來(lái)看看JS的繼承方式。JS中繼承方式與寫(xiě)類(lèi)方式息息相關(guān)。不同的寫(xiě)類(lèi)方式造成不同的繼承方式。各種流行JavaScript庫(kù)繼承方式也各不相同。從最簡(jiǎn)單的復(fù)用開(kāi)始。
1、構(gòu)造函數(shù)方式寫(xiě)類(lèi),通過(guò)方法調(diào)用復(fù)制父類(lèi)屬性/字段到子類(lèi) 實(shí)現(xiàn)繼承
這里父類(lèi),子類(lèi)都采用構(gòu)造函數(shù)方式寫(xiě),不用原型。子類(lèi)調(diào)用父類(lèi)函數(shù)來(lái)復(fù)制父類(lèi)的屬性。
- /**
- * 父類(lèi)Polygon:多邊形
- * @param {Object} sides
- */
- function Polygon(sides) {
- this.sides = sides;
- this.setSides = function(s) {this.sides=s;}
- }
- /**
- * 子類(lèi)Triangle:三角形
- */
- function Triangle() {
- this.tempfun = Polygon;//父類(lèi)引用賦值給子類(lèi)的一個(gè)屬性tempfun
- this.tempfun(3);//調(diào)用
- delete this.tempfun;//刪除該屬性
- this.getArea = function(){};
- }
- //new個(gè)對(duì)象
- var tri = new Triangle();
- console.log(tri.sides);//繼承的屬性
- console.log(tri.setSides);//繼承的方法
- console.log(tri.getArea);//自有的方法
- //缺點(diǎn)是對(duì)于Triangle的實(shí)例對(duì)象用instanceof為父類(lèi)Polygon時(shí)是false
- console.log(tri instanceof Triangle);//true
- console.log(tri instanceof Polygon);//false
因?yàn)?JavaScript中具名函數(shù)的多種調(diào)用方式 ,子類(lèi)還可以有以下的多種實(shí)現(xiàn)方式。只是在子類(lèi)中調(diào)用父類(lèi)方法不同而已。
- function Triangle() {
- Polygon.call(this,3); //call方式調(diào)用父類(lèi)
- this.getArea = function(){};
- }
- function Triangle() {
- Polygon.apply(this,[3]); //apply方式調(diào)用父類(lèi)
- this.getArea = function(){};
- }
- function Triangle() {
- var temp = new Polygon(3); //new方式調(diào)用父類(lèi)
- for(atr in temp) { //全部復(fù)制給子類(lèi)
- this[atr] = temp[atr];
- }
- this.getArea = function(){};
- }
這種方式的缺點(diǎn)是子類(lèi)的實(shí)例對(duì)象用instanceof檢查父類(lèi)時(shí)總是false。這與java中繼承"is a "的關(guān)系是違背的。
2、原型方式寫(xiě)類(lèi),原型方式繼承
core JS自身的對(duì)象系統(tǒng)就是采用原型方式(prototype based)繼承的?;蛘哒f(shuō)core JS沒(méi)有采用常見(jiàn)的類(lèi)繼承(class based)系統(tǒng),而是使用原型繼承來(lái)實(shí)現(xiàn)自己的對(duì)象系統(tǒng)。工作中我們也可以用原型方式來(lái)實(shí)現(xiàn)繼承,代碼復(fù)用以構(gòu)建自己的功能模塊。
- /**
- * 父類(lèi)Polygon:多邊形
- *
- */
- function Polygon() {}
- Polygon.prototype.sides = 0;
- Polygon.prototype.setSides = function(s) {this.sides=s;}
- /**
- * 子類(lèi)Triangle:三角形
- */
- function Triangle() {}
- Triangle.prototype = new Polygon(); //這是原型繼承關(guān)鍵的一句
- Triangle.prototype.getArea = function(){}
- //new個(gè)對(duì)象
- var tri = new Triangle();
- console.log(tri.sides);//繼承的屬性
- console.log(tri.setSides);//繼承的方法
- console.log(tri.getArea);//自有方法
- //instanceof測(cè)試
- console.log(tri instanceof Triangle);//true,表明該對(duì)象是三角形
- console.log(tri instanceof Polygon);//true,表明三角形也是多邊形
雖然從輸出可以看出子類(lèi)繼承了父類(lèi)Polygon的屬性sides和方法setSides,但sides是0,怎么會(huì)是三角形呢。還得調(diào)用下tri.setSides(3)使之成為三角形。這樣似乎很不方便。不能傳參數(shù),即是原型方式的缺點(diǎn)。優(yōu)點(diǎn)是正確的維護(hù)了"is a"的關(guān)系。
3、組合構(gòu)造函數(shù)/原型方式寫(xiě)類(lèi),采用前面種方式繼承
這種方式父類(lèi),子類(lèi)的屬性都掛在構(gòu)造函數(shù)里,方法都掛在原型上。
- /**
- * 父類(lèi)Polygon:多邊形
- */
- function Polygon(sides) {
- this.sides = sides;
- }
- Polygon.prototype.setSides = function(s) {this.sides=s;}
- /**
- * Triangle 三角形
- * @param {Object} base 底
- * @param {Object} height 高
- */
- function Triangle(base,height) {
- Polygon.call(this,3);//復(fù)制父類(lèi)屬性給自己
- this.base = base;
- this.height = height;
- }
- Triangle.prototype = new Polygon();//復(fù)制父類(lèi)方法給自己
- Triangle.prototype.getArea = function(){ //***定義自己的方法
- return this.base*this.height/2;
- }
- //new個(gè)對(duì)象
- var tri = new Triangle(12,4);
- console.log(tri.sides);//繼承的屬性
- console.log(tri.setSides);//繼承的方法
- console.log(tri.base);//自有屬性
- console.log(tri.height);//自有屬性
- console.log(tri.getArea);//自有方法
- //instanceof測(cè)試,表明正確的維護(hù)了"is a"的關(guān)系
- console.log(tri instanceof Triangle);//true,表明該對(duì)象是三角形
- console.log(tri instanceof Polygon);//true,表明三角形也是多邊形
#p#
這篇開(kāi)始寫(xiě)幾個(gè)工具函數(shù)實(shí)現(xiàn)類(lèi)的擴(kuò)展。每個(gè)工具函數(shù)都是針對(duì)特定的寫(xiě)類(lèi)方式(習(xí)慣)。這篇按照構(gòu)造函數(shù)方式寫(xiě)類(lèi):屬性(字段)和方法都掛在this上。以下分別提供了個(gè)類(lèi),分別作為父類(lèi)和子類(lèi)。
- // 父類(lèi)Person
- function Person(nationality) {
- this.nationality = nationality;
- this.setNationality = function(n) {this.nationality=n;};
- this.getNationality = function() {return this.nationality;};
- }
- // 類(lèi)Man
- function Man(name) {
- this.name = name;
- this.setName = function(n){this.name=n;};
- this.getName = function(){return this.name;};
- }
繼承工具函數(shù)一
- /**
- * @param {Function} subCls 子類(lèi)
- * @param {Function} superCls 父類(lèi)
- * @param {Object} param 父類(lèi)構(gòu)造參數(shù)
- */
- function extend(subCls,superCls,param) {
- superCls.call(subCls.prototype,param);
- }
使用如下
- extend(Man,Person,'China');
- var m = new Man('jack');
- console.log(m.nationality);//China
- console.log(m.setNationality('Japan'));
- console.log(m.getNationality('Japan'));//Japan
輸出可以看到Man繼承了Person的屬性及所有方法。這種繼承方式于java的很不一樣哦,
- class Animal {
- int legs;
- Animal(int l) {
- legs = l;
- }
- int getLegs() {
- return legs;
- }
- }
- public class Person extends Animal{
- //屬性(字段)
- String name;
- //構(gòu)造方法(函數(shù))
- Person(int legs, String name) {
- super(legs);//調(diào)用父類(lèi)構(gòu)造器
- this.name = name;
- }
- //方法
- String getName() {
- return this.name;
- }
- public static void main(String[] args) {
- Person p = new Person(2,"jack");
- System.out.println(p.legs);
- }
- }
Java中,子類(lèi)Person在自身構(gòu)造方法中調(diào)用父類(lèi)構(gòu)造方法super(legs),創(chuàng)建對(duì)象的時(shí)候直接將父類(lèi)構(gòu)造參數(shù)legs:2傳進(jìn)去,不僅僅只傳自己的name:jack。上面JavaScript繼承是在extend時(shí)傳父類(lèi)構(gòu)造參數(shù)(extend函數(shù)的第三個(gè)參數(shù)),而不是在new Man時(shí)將父類(lèi)構(gòu)造參數(shù)傳過(guò)去。好,模擬Java來(lái)實(shí)現(xiàn)下extend,這里巧妙的在子類(lèi)上暫存了父類(lèi)引用。
繼承工具函數(shù)二
- /**
- * @param {Function} subCls
- * @param {Function} superCls
- */
- function extend(subCls,superCls) {
- subCls.supr = superCls;
- }
還是以Person為父類(lèi),來(lái)實(shí)現(xiàn)子類(lèi)Woman
- function Woman(nationality,name) {
- Woman.supr.call(this,nationality);//和java有點(diǎn)類(lèi)似哦,在子類(lèi)中調(diào)用父類(lèi)構(gòu)造器
- this.name = name;
- this.setName = function(n){this.name=n;};
- this.getName = function(){return this.name;};
- }
extend(Woman,Person);
***,創(chuàng)建對(duì)象的方式和java也類(lèi)似,即new的時(shí)候同時(shí)將父類(lèi)構(gòu)造參數(shù)(nationality:Japan)傳進(jìn)去。
- var w = new Woman('Japan','lily');
- console.log(w.nationality);//Japan
- w.setNationality('U.S.A');
- console.log(w.getNationality());//U.S.A
繼承工具函數(shù)三
- /**
- * @param {Function} subCls
- * @param {Function} superCls
- */
- function extend(subCls,superCls) {
- subCls.prototype = new superCls();
- }
父類(lèi),按原型方式寫(xiě),即屬性和方法都掛在原型上。
- /**
- * 父類(lèi)Person
- */
- function Person(){}
- Person.prototype.nationality = 'China';
- Person.prototype.getNationality = function() {return this.nationality;}
- Person.prototype.setNationality = function(n) { this.nationality = n;}
子類(lèi)繼承與父類(lèi)
- function Man() {}
- extend(Man,Person);
繼承父類(lèi)的屬性和方法后,再添加子類(lèi)自有屬性,方法
- Man.prototype.name = 'jack';
- Man.prototype.getName = function() { return this.name;}
- Man.prototype.setName = function(n) { this.name=n;}
測(cè)試如下,
- var m = new Man();
- console.log(m);
- console.log(m instanceof Person);
可以看到這種寫(xiě)類(lèi)方式,繼承方式完全采用原型機(jī)制。
#p#
繼承工具函數(shù)四
這種方式是目前比較流行的,51ditu網(wǎng)站的開(kāi)發(fā)就是按照這種模式的。
- /**
- * @param {Function} subCls 子類(lèi)
- * @param {Function} superCls 父類(lèi)
- */
- function extend(subCls,superCls) {
- //暫存子類(lèi)原型
- var sbp = subCls.prototype;
- //重寫(xiě)子類(lèi)原型--原型繼承
- subCls.prototype = new superCls();
- //重寫(xiě)后一定要將constructor指回subCls
- subCls.prototype.constructor = subCls;
- //還原子類(lèi)原型
- for(var atr in sbp) {
- subCls.prototype[atr] = sbp[atr];
- }
- //暫存父類(lèi)
- subCls.supr = superCls;
- }
按 構(gòu)造函數(shù)+原型 方式寫(xiě)類(lèi),即屬性掛在this上,方法掛在prototype上。
- /**
- * 父類(lèi)Person
- */
- function Person(nationality){
- this.nationality = nationality;
- }
- Person.prototype.getNationality = function() {return this.nationality;}
- Person.prototype.setNationality = function(n) { this.nationality = n;}
- /**
- * 子類(lèi)Man
- */
- function Man(nationality,name) {
- Man.supr.call(this,nationality); //很重要的一句,調(diào)用父類(lèi)構(gòu)造器
- this.name = name;
- }
- Man.prototype.getName = function() {return this.name;}
- Man.prototype.setName = function(n) {this.name=n;}
注意子類(lèi)Man中要顯示的調(diào)用父類(lèi)構(gòu)造器已完成父類(lèi)的屬性/字段拷貝。
extend調(diào)用,創(chuàng)建Man的實(shí)例
- extend(Man,Person);
- var m = new Man('USA','jack');
- console.log(m);
- m.setName('lily');
- console.log(m.name);
繼承工具函數(shù)五
- /**
- * @param {String} className
- * @param {String/Function} superClass
- * @param {Function} classImp
- */
- function $class(className, superClass, classImp){
- if(superClass === "") superClass = Object;
- var clazz = function(){
- return function(){
- if(typeof this.init == "function"){
- this.init.apply(this, arguments);
- }
- };
- }();
- var p = clazz.prototype = new superClass();
- var _super = superClass.prototype;
- window[className] = clazz;
- classImp.apply(p, [_super]);
- }
定義父類(lèi)Person
- /**
- * 父類(lèi) Person
- */
- $class('Person','',function(){
- this.init = function(name){
- this.name = name;
- };
- this.getName = function(){
- return this.name;
- };
- this.setName = function(name){
- this.name = name;
- }
- });
子類(lèi)Man
- /**
- * 子類(lèi) Man
- */
- $class('Man', Person, function(supr){
- this.init = function(name, age){
- supr.init.apply(this,[name]); // 該句很重要
- this.age = age;
- };
- this.getAge = function(){
- return this.age;
- };
- this.setAge = function(age){
- this.age = age;
- };
- });
- var m = new Man('Jack',25);
- console.log(m.name); // Jack
- console.log(m.age); // 25
從輸出看可以看到子類(lèi)Man的確繼承了父類(lèi)的屬性和方法。
原文鏈接:http://www.cnblogs.com/snandy/archive/2011/03/09/1977804.html
【編輯推薦】
- 如何編寫(xiě)高質(zhì)量的Javascript代碼
- 淺析JavaScript的寫(xiě)類(lèi)方式
- JavaScript跨域總結(jié)與解決辦法
- JavaScript版幾種常見(jiàn)排序算法分享
- 10個(gè)令人驚奇的HTML5和JavaScript效果
本文名稱(chēng):淺析JavaScript繼承方式
當(dāng)前路徑:http://m.fisionsoft.com.cn/article/dhseepd.html


咨詢
建站咨詢
