js中实现继承的方式主要是通过原型链的方式,这边就大概讲下各种方法的实现
1.原型链
原型链的原理就是让子对象的原型指向父对象的一个实例,然后再在子对象的原型里添加自己的方法。这样就可以做到引用父对象的方法属性的同时使用自己的方法属性。看一下代码:
function Person(){ this.name = 'super'; } Person.prototype.getName = function(){ return this.name; } function Someone(){ this.son = true; } Someone.prototype = new Person(); Someone.prototype.getSonStatus = function(){ alert(this.son); } var instance = new Someone(); alert(instance.getName());
通过将Someone的原型指向Person的一个实例,使得随后创建的instance可以访问Someone中的属性方法。在例子中,要是Person的原型也指向另外一个对象的实例,那么这就像一个链条一样,一层层的指代引用。在读取instance的方法属性时,总是先遍历自己的属性方法,再查找原型中的方法属性,接着按照原型链的顺序,一层层向上寻找。
原型链实现的继承有很多问题,首先是由于原型的共享引起的,即改变继承来的属性的内容后,所有的实例里的该属性都会随着改变。其次,就是我们无法向超类型的构造函数传递参数,对于简单的继承可能无所谓,但是对于大多数的继承情况,无法传递参数就会使许多功能无法实现。
2.借用构造函数
借用构造函数的技术就可以解决原型中包含引用类型值所带来的问题,这种类型的实现模式主要是在子类型的构造函数中调用超类型的构造函数,这样来实现对超类型中的属性进行继承,如下所示
function Person(name){ this.name = name; } Person.prototype.getName = function(){ return this.name; } function Someone(){ this.son = true; Person.call(this,'Peter'); } Someone.prototype.getSonStatus = function(){ alert(this.son); } var instance = new Someone(); alert(instance.name);
这样就可以做到向超类传递参数,但是在最后如果调用instance.getName()的话会报错,这是因为我只在子类的构造函数中运行了超类的构造函数,但是并未得到超类的prototype,这使得超类的一些方法无法继承。这时候我们就需要结合以上两种方式,稍微做出修改,就可以得到一个真正的继承了。代码如下:
function Person(name){ this.name = name; } Person.prototype.getName = function(){ return this.name; } function Someone(){ this.son = true; Person.call(this,'Peter'); } Someone.prototype = new Person(); Someone.prototype.constructor = Someone; Someone.prototype.getSonStatus = function(){ alert(this.son); } var instance = new Someone(); alert(instance.getName());
其中,Someone.prototype里又加了一个constructor属性,让它指向Someone。
3.寄生组合继承
在上个例子中,Someone.prototype = new Person()中,我们无非只是需要Person中的prototype而已,其中的属性已经通过call的方式实现继承了,在这里多做了一个属性的继承,而且直接new Person()来继承,有时候会因为构造函数缺少变量导致运行错误。所以我们需要一个只继承原型的方法,这就是寄生组合了。看以下代码:
function object(o){ function F(){}; F.prototype = o; return new F(); } function inherit(superType,subType){ var proto = object(superType.prototype); proto.constructor = subType; subType.prototype = proto; }
首先是object函数,它可以根据传进来的对象,将之加到临时构造函数的[[prototype]]上,然后返回实例,那么返回的实例就是空属性,但是prototype里面的内容就是你传进来的对象。
这样,再看inherit函数,proto里保存的对象就是空属性,但是prototype是superType.prototype;然后重新设定constructor,再将其替换原来的subType的prototype,这样,纯原型的继承就实现了,我们把所有代码集合在一起,看一个例子:
function object(o){ function F(){}; F.prototype = o; return new F(); } function inherit(superType,subType){ var proto = object(superType.prototype); proto.constructor = subType; subType.prototype = proto; } //以上用于继承函数的原型 function Person(name){ this.name = name; } Person.prototype.getName = function(){ return this.name; } function Someone(){ this.son = true; Person.call(this,'Peter'); //继承超类构造函数中的属性 } inherit(Person,Someone); Someone.prototype.getSonStatus = function(){ alert(this.son); } var instance = new Someone(); alert(instance.getName());