ECMAScript只支持实现继承(继承实际的方法),主要依靠原型链来实现。
1、原型链
基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
示例:
function SuperType(){
this.property = true; //实例属性 } SuperType.prototype.getSuperValue = function(){//原型方法 return this.property; } function SubType(){ this.subproperty = false; } //SubType继承了SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function(){ return this.subproperty; } var instance = new SubType(); console.log(instance.getSuperValue()); //true console.log(instance.constructor); //SuperType说明:
以上代码创建了两个类型:SuperType 和 SubType。这两个类型都有各自的属性和方法,SubType继承了SuperType,继承是通过创建SuperType的实例,并把该实例赋给SubType的prototype。实现的本质是重写了SubType原型对象,代之以一个新类型的实例。
SubType的新原型具有SuperType所有的属性和方法,同时其内部还有一个指针[[Prototype]],指向SuperType的原型,最终结果是instance指向了SubType的原型,SubType的原型又指向了SuperType的原型。
instance.constructor指向的是SuperType,这是因为原来的SubType.prototype中的constructor被重写了。(实际上不是SubType的原型的constructor属性被重写了,而是SubType的原型指向了SuperType,而这个原型的constructor属性指向的是SuperType。)
所有引用类型默认都继承了Object,这个继承也是通过原型链类实现的。所有函数的默认原型都是Object的实例,默认原型都会包含一个内部指针,指向Object.prototype。
注意:在通过原型链实现继承时,不能使用字面量对象创建原型方法,这样会重写原型链,把我们的原型链切断。
原型链的问题:(1)、包含引用类型值的原型;
(2)、创建子类型的实例时,不能向超类型的构造函数中传递参数。
2、借用构造函数
也叫伪造对象或者经典继承,基本思想是在子类型构造函数内部调用超类型构造函数。
function SuperType(name){
this.name = name; this.colors = ['red', 'blue', 'green']; } function SubType(){ //继承了SubType,同时还传递了参数 SuperType.call(this, 'Nick'); //实例属性 this.age = 12; } var instance1 = new SubType(); instance1.colors.push('gray'); console.log(instance1.name); console.log(instance1.age); var instance2 = new SubType(); console.log(instance2.colors);借用构造函数的问题:方法都在构造函数中定义,因此函数复用就无从谈起了。而且,在超类型的原型中定义的方法,对子类型而言也是不可见的。
3、组合继承
也叫伪经典继承,基本思想是使用原型链实现对原型属性和方法的继承,通过借用构造函数实现对实例属性的继承,这样,即通过在原型上定义方法实现了函数复用,又能保证每个实例都有它自己的属性。
function SuperType(name){
this.name = name; this.colors = ['red', 'yellow', 'blue']; } SuperType.prototype.sayName = function(){ return this.name; } function SubType(name, age){ //继承属性 SuperType.call(this, name); this.age = age; } //继承方法 SubType.prototype = new SuperType(); SubType.prototype.sayAge = function(){ return this.age; } var instance1 = new SubType('Tom', 12); instance1.colors.push('black'); console.log(instance1.colors);//['red', 'yellow', 'blue', 'black'] console.log(instance1.sayAge());//12 console.log(instance1.sayName());//Tom var instance2 = new SubType('Lucy',21); console.log(instance2.colors);//['red', 'yellow', 'blue'] console.log(SubType.prototype.isPrototypeOf(instance2));//true console.log(SuperType.prototype.isPrototypeOf(instance2));//true组合继承是JavaScript常用的继承模式,但是也有不足。
4、寄生组合式继承
通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。
//借助原型基于已有对象创建新对象(本质上讲,object()对传入的对象执行了一次浅复制)
function object(o){ function F(){} F.prototype = o; return new F(); } //使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型 function inheritPrototype(SuperType, SubType){ var prototype = object(SuperType.prototype);//创建超类型原型的一个副本 prototype.constructor = SubType;//增强对象,弥补因重写原型而失去的默认的constructor属性 SubType.prototype = prototype; } function SuperType(name){ this.name = name; this.colors = ['blue', 'red', 'green']; } SuperType.prototype.sayName = function(){ return this.name; } function SubType(name, age){ SuperType.call(this, name); this.age = age; } inheritPrototype(SuperType, SubType); SubType.prototype.sayAge = function(){ return this.age; } var instance = new SubType('Tom', 12); instance.colors.push('black'); console.log(instance.colors); delete instance.name; console.log(instance.name); var instance = new SubType('Lucy', 33); console.log(instance.colors);普遍认为寄生组合式继承是引用类型最理想的继承范式。