在Javascript中,一切皆是对象,所谓的类也只是用来模拟其它面向对象语言的class的对象而已,例如:
function MyClass() {}//定义一个类 MyClass.call();//其实这个类本身也是一个对象
在上面的代码中,并没有为MyClass定义call方法,但是却可以调用call方法,其原因就是JavaScript的原生对象在发挥作用。通过function关键字定义一个类,实质上就是实例化一个Function的实例,因为Javascript在Function的原生对象中定义了call方法,所以即使MyClass的定义是空的,但是还是可以调用call方法!由此,我们就可以看到继承的影子!
在原生对象中定义的方法call被继承到了MyClass中,那么只要修改一下原生对象,那么就可以模拟出面向对象的继承机制了。
function MyBaseClass() { this.Add = function(a, b) { return a + b; } } function MyClass() { } MyClass.prototype = new MyBaseClass(); var obj = new MyClass(); var X = obj.Add(3, 4);
只要把MyClass的原生对象指向基类MyBaseClass的实例对象,那么MyClass就可以继承MyBaseClass中的方法,从而实现了继承机制!
多继承
任何对象的直接原生对象都只有一个,那么如果想实现多继承该怎么办呢?可以采用两种办法:
方法一:链状继承
function MyBaseClassA() { this.Add = function(a, b) { return a + b; } } function MyBaseClassB() { this.Sub = function(a, b) { return a - b; } } function MyClass() { } MyBaseClassB.prototype = new MyBaseClassA(); MyClass.prototype = new MyBaseClassB(); var obj = new MyClass(); var X = obj.Add(3, 4); var Y = obj.Sub(9, 4);
方法二:拷贝继承
function MyBaseClassA() { this.Add = function(a, b) { return a + b; } } function MyBaseClassB() { this.Sub = function(a, b) { return a - b; } } function MyClass() { } var A = new MyBaseClassA(); for (var method in A) { MyClass.prototype[method] = A[method]; } var B = new MyBaseClassB(); for (var method in B) { MyClass.prototype[method] = B[method]; } var obj = new MyClass(); var X = obj.Add(3, 4); var Y = obj.Sub(9, 4);
继承的本质就是属性拷贝,所以只要手动拷贝基类的所有属性,那么就可以实现多继承了,对于拷贝属性的代码可以封装一下,便于调用:
var Extend = function(DeriveClass,BaseClass) { var o = new BaseClass(); for (var method in o) { DeriveClass.prototype[method] = o[method]; } } Extend(MyClass, MyBaseClassA); Extend(MyClass, MyBaseClassB);
继承是实现了,但是如果有构造参数有又该怎么处理呢?答案就是在派生类中调用基类构造就可以了:
function MyBaseClass(a, b) { this.A = a; this.B = b; this.Add = function() { return this.A + this.B; } } function MyClass(a,b,c) { MyBaseClass.call(this, a, b);//调用基类的构造 } var Extend = function(DeriveClass, BaseClass) { var o = new BaseClass(); for (var method in o) { DeriveClass.prototype[method] = o[method]; } } Extend(MyClass, MyBaseClass); var obj = new MyClass(4,5,8); var X = obj.Add(); alert(X); //结果:9
call和apply方法是经常用到的两个方法,其作用就是在另一个域内调用方法!
继承机制带来的另一个问题就是同名方法隐藏问题!Javascript对象都是由原型对象生成,原型对象也有其自己的原型对象,这样就构成了一条原型对象链,对象链的顶端指向Object原生对象,末端就是对象本身,如果对象链中存在同名的方法,那么对象链末端的方法访问优先级高于顶端的优先级,因此派生类的方法总是隐藏基类的方法:
var BaseClass=function(){ this.Hello=function(){ return "A"; } this.say=function(){ return "Saying A"; } } var BaseObj = new BaseClass(); var X = BaseObj.Hello(); alert(X); //结果:A BaseClass.prototype.Hello=function(a,b){ return "B"; } var Y = BaseObj.Hello(); alert(Y); //结果:A var MyClass = function(){ this.Hello=function(){ return "C"; } } var Extend = function(DeriveClass, BaseClass) { var o = new BaseClass(); for (var method in o) { DeriveClass.prototype[method] = o[method]; } } Extend(MyClass, BaseClass); var MyObj = new MyClass(); var Z = MyObj.Hello(); alert(Z); //结果:C MyClass.prototype.Hello=function(){ return "D"; } MyClass.prototype.say=function(){ return "Saying D"; } var K = MyObj.Hello(); alert(K); //结果:C var L = MyObj.say(); alert(L); //结果:Saying D
方法隐藏的唯一准则就是原型对象链的顺序,方法是否会被隐藏与实现继承的机制相关!
以后再说一说Javascript面向对象的多态特性!