类继承:inherits
假设有已定义好的超类(父类)SuperClass和待继承的子类SubClass,于是,可以定义如下的方法来实现类继承
inherits方法的定义
如下的这个inherits方法,其实现思想主要参考《Pro JavaScript Design Patterns》【Ross Harmes and Dustin Diaz】
/** * 这个方法用来实现类继承 * @param {function} subClass 待继承的子类 * @param {function} superClass 待被继承的父类 * @exception {Error} 参数不合法时抛出异常 */ var inherits = function(subClass,superClass){ if(arguments.length !== 2){ throw new Error("必须明确的指定子类和父类"); } for(var i = 0,n = arguments.length;i < n;i++){ if(typeof arguments[i] !== "function"){ throw new Errorr("所给的子类和父类必须都是function"); } } var F = function(){}; F.prototype = superClass.prototype; subClass.prototype = new F(); subClass.prototype.constructor = subClass; };
该方法的内部会进行严格的参数合法性检查,参数必须是两个,即一个子类,一个父类;另外还要求两个参数都必须是function类型,否则抛出异常,宣告继承失败
inherits方法的使用
▲第一步
/** * 定义父类 */ var SuperClass = function(){}; SuperClass.prototype = { name : "赵先烈", talk : function(){ alert("我的名字叫" + this.name); } }; /** * 这里定义子类 */ var SubClass = function(){}; //实施继承 inherits(SubClass,SuperClass); //创建子类并从调用从子类继承过来的方法 var oSub = new SubClass(); oSub.talk(); //这里输出:我的名字叫赵先烈
从程序的执行结果可以看出,这种继承方法是将父类中的全部内容(除构造器以外)拷贝到子类中来,顺利的完成了类继承。
▲第二步
/** * 这里定义父类 */ var SuperClass = function(){ this.name = "赵先烈"; this.talk = function(){ alert("我的名字叫" + this.name); }; }; /** * 这里定义子类 */ var SubClass = function(){ //这是一个空类 }; //实施继承 inherits(SubClass,SuperClass); //创建子类对象,并调用从父类继承过来的方法 var oSub = new SubClass(); oSub.talk(); //按照继承的概念,这里应该输出:我的名字叫赵先烈
程序真正执行后,才发现发生异常了,原因:在oSub实例中不存在talk方法。不是已经通过inherits方法实现继承了么,为什么会没有这个 方法呢?探究其原因,是因为在inherits的内部,只是将SuperClass的原型链原样的拷贝给了SubClass,而不在SuperClass 原型链中的属性及方法,子类就拿不到了。
那么,既要支持第一种继承,也要能满足第二种,我们只能再次对inherits方法进行升级了,将其改为如下形式:
/** * 这个方法用来实现类继承 * @param {function} subClass 待继承的子类 * @param {function} superClass 待被继承的父类 * @exception {Error} 参数不合法时抛出异常 */ var inherits = function(subClass,superClass){ if(arguments.length !== 2){ throw new Error("必须明确的指定子类和父类"); } for(var i = 0,n = arguments.length;i < n;i++){ if(typeof arguments[i] !== "function"){ throw new Errorr("所给的子类和父类必须都是function"); } } subClass.prototype = new superClass(); subClass.prototype.constructor = subClass; };
经过这样的改动,第一步和第二步中的情况就都能顺利的正常的执行了。
▲第三步
如果在第二步的基础上,将代码中的SubClass.prototype改为如下形式:
SubClass.prototype = { walk : function(){ alert("偶也,我可以走路了哎"); }, talk : function(){ alert("我的名字不叫" + this.name); }
};
再执行如下代码:
inherits(SubClass,SuperClass); var oSub = new SubClass(); oSub.talk(); //这里仍然输出:我的名字叫赵先烈 //重点在下面这句 oSub.walk(); //这里会抛出异常,告诉我们,walk方法不存在
分析这个结果,oSub.talk()的输出应该为:我的名字不叫赵先烈。并且oSub.walk()也应该有输出,程序应该正常执行才对。但是,为什么会有这样的运行结果呢?
这到底是为什么呢?相信你一定早就发现了,在inherits方法中有这样一句关键的代码:
subClass.prototype = new superClass();
这里已经告诉我们,原来,子类中的原型链已经被完全替换了,其实仔细说起来,这样做是有违继承的原则的(一般不都是子类覆盖父类么?怎么 反过来了呢)。
但是这样的问题我们始终要解决,必须让这个所谓的继承能同时满足以上三种情况都能正常执行。苦思冥想后,决定改成这样:
/** * 这个方法用来实现类继承 * @param {function} subClass 待继承的子类 * @param {function} superClass 待被继承的父类 * @exception {Error} 参数不合法时抛出异常 */ var inherits = function(subClass,superClass){ if(arguments.length !== 2){ throw new Error("必须明确的指定子类和父类"); } for(var i = 0,n = arguments.length;i < n;i++){ if(typeof arguments[i] !== "function"){ throw new Errorr("所给的子类和父类必须都是function"); } } var oSuper = new superClass(); for(var key in oSuper){ if(!subClass.prototype[key]){ subClass.prototype[key] = oSuper[key]; } } subClass.prototype.constructor = subClass; };
OK,通过这样的升级,再次运行上面的代码,顺利通过了。
上面这个通过两次升级的inherits方法也是目前比较稳定的版本了,在后续的文章中所指的inherits方法,就是这个了。
本文旨在共同探讨Javascript中类的继承方式,只是其中的一种模拟方式,模拟之不当,还请见谅。
欢迎提出宝贵的升级建议。