在Java、C++、C#等OO语言中,都支持两种继承方式:接口继承和实现继承。接口继承制继承方法签名,实现继承则继承实际的方法和属性。在SCMAScript中,由于函数没有签名,所以无法实现接口继承,只支持实现继承。
实现继承主要依靠原型链来实现。
一、原型链
原型链是利用原型让一个引用类型继承另一个引用类型的方法,在DOM笔记(十二):又谈原型对象中,描述了构造函数、实例和原型之间的关系:
每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,而每个实例都包含一个指向原型对象的内部指针。如果将原型对象作为另一个构造函数的实例,会如何?
function SuperType() { this.property = true; } SuperType.prototype.getSuperValue=function() { return this.property; }; function SubType() { this.subproperty = false; } // 继承SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue=function() { return this.subproperty; }; var instance = new SubType(); alert(instance.getSuperValue()); //true
在DOM笔记(十一):JavaScript对象的基本认识和创建中提到了,Object类型是所有对象的根元素。所以,SubType也是继承了Object,内部有一个指针指向Object的原型。 现在,他们的关系变成了这样:
当instance调用getSuperValue()时,会进行搜索:1)在实例中搜索;2)在实例中找不到,就在SubType.prototype中搜索;3)在SubType.prototype中找不到,就在SuperType.prototype中找。找到后就停止搜索,返回结果值。但instance调用toString()时,就一直会搜索到原型链的末端—Object.prototype。
根据原型链,需要注意的是,instance.constructor不再指向SubType,而是指向了SuperType。
二、原型继承小结
1、确定原型跟实例的关系
在DOM笔记(十二):又谈原型对象中提到了isPrototypeOf()方法,另外也可以用instanceof确立这种关系的存在。
//均返回true alert(instance instanceof Object); alert(instance instanceof SuperType); alert(instance instanceof SubType); alert(Object.prototype.isPrototypeOf(instance)); alert(SuperType.prototype.isPrototypeOf(instance)); alert(SubType.prototype.isPrototypeOf(instance));
2、当在子类中定义与父类中同名的属性和方法时,父类中对应的属性和方法会被屏蔽。
function SuperType() { this.property = true; } SuperType.prototype.getSuperValue=function() { return this.property; }; function SubType() { this.property = false; //同名属性 } // 继承SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue=function() { return this.property; }; var instance = new SubType(); alert(instance.getSuperValue()); //false
function SuperType() { this.property = true; } SuperType.prototype.getSuperValue=function() { return this.property; }; function SubType() { this.subproperty = false; } // 继承SuperType SubType.prototype = new SuperType(); // 使用字面量添加新方法,导致上一行无效 SubType.prototype={ getSubValue:function() { return this.subproperty; } }; var instance = new SubType(); alert(instance.getSuperValue()); //error:undefined is not a function
4、原型继承存在两个问题:1)引用类型的原型属性会被所有实例共享;2)原型继承时,不能通过子类的实例向父类的构造函数中传递参数。
function SuperType() { this.colors = ["red","blue","white"]; } function SubType(){ } // 继承SuperType SubType.prototype = new SuperType(); var instance1 = new SubType(); instance1.colors.push("black"); alert(instance1.colors); //red,blue,white,black var instance2 = new SubType(); alert(instance2.colors); //red,blue,white,black
instance1和instance2共享了colors数组属性(引用类型)。
三、其他继承方式
1、借用构造函数
利用原型继承时,引用类型的原型属性会被所有实例共享。但是借用构造函数就能避免这种问题,并且能够利用apply()和call()传递参数给父类的构造函数:
function SuperType() { this.colors = ["red","blue","white"]; } function SubType() { // 继承SuperType SuperType.call(this); } var instance1 = new SubType(); instance1.colors.push("black"); alert(instance1.colors); //red,blue,white,black var instance2 = new SubType(); alert(instance2.colors); //red,blue,white
function SuperType(name) { this.name=name; this.colors = ["red","blue","white"]; } SuperType.prototype.sayName=function() { alert(this.name); }; function SubType(name,age) { // 继承SuperType属性,并传递参数给父类的构造函数 SuperType.call(this,name); //第一次调用SuperType() this.age=age; } // 继承SuperType方法 SubType.prototype = new SuperType(); //第二次调用SuperType() SubType.prototype.constructor = SubType; SubType.prototype.sayAge=function() { alert(this.age); }; var instance1 = new SubType("dwqs",20); instance1.colors.push("black"); alert(instance1.colors); //red,blue,white,black instance1.sayName(); //dwqs instance1.sayAge(); //20 var instance2 = new SubType("i94web",25); alert(instance2.colors); //red,blue,white instance2.sayName(); //i94web instance2.sayAge(); //25
function object(o) { function F(){} F.prototype=o; return new F(); }
var person={
name:“dwqs”,
colors:["red","blue","white"]
};
var per1 = object(person);
alert(per1.name); //dwqs
// 覆盖已有的属性
per1.name = “i94web”;
per1.age = 20; //新增属性
alert(per1.age); //20
per1.colors.push(“black”);
alert(per1.name); //i94web
alert(per1.colors); //red,blue,white,black
var per2 = object(person);
alert(per2.name); //dwqs
// 所有实例共享引用类型的属性
alert(per2.colors); //red,blue,white,black
其混乱关系如下:
per1 和per2会共享colors属性(引用类型)。在ECMAScript 5中,Object.create(obj,[props])规范了这种方式:obj是已经存在的对象,props可选,是一个包含额外属性的对象,格式 与Object.defineProperties()的第二个参数格式一样。
var person={ name:"dwqs", colors:["red","blue","white"] }; var per1 = Object.create(person,{ age:{ value:20 } }); alert(per1.age); //20
4、寄生式继承
该方式与原型式继承差不多,也要借用object函数,然后在内部已某种方式来增强对象。
function object(o) { function F(){} F.prototype=o; return new F(); } var person={ name:"dwqs", colors:["red","blue","white"] }; function createObj(obj) { var clone = object(obj); // 增强对象 clone.age = 20; clone.Hello=function() { alert("Hello,my age is "+this.age); }; return clone; } var per1 = createObj(person); per1.Hello(); //Hello,my age is 20
5、寄生组合式继承
在第2中方式,组合继承虽然时JavaScript钟最常用的方式,但是也存在不足,上面已经提到,不再赘述。而寄生组合式继承能解决这两个不足,其基本模式如下:
function object(o) { function F(){} F.prototype=o; return new F(); } function inheritPrototype(subType,superType) { var obj = object(superType.prototype); //创建对象 obj.constructor = subType; //增强对象 subType.prototype = obj; //指定对象 }
重写组合继承中的示例:
function object(o) { function F(){} F.prototype=o; return new F(); } function inheritPrototype(subType,superType) { var obj = object(superType.prototype); //创建对象 obj.constructor = subType; //增强对象 subType.prototype = obj; //指定对象 } function SuperType(name) { this.name=name; this.colors = ["red","blue","white"]; } SuperType.prototype.sayName=function() { alert(this.name); }; function SubType(name,age) { // 继承SuperType属性,并传递参数给父类的构造函数 SuperType.call(this,name); this.age=age; } inheritPrototype(SubType,SuperType); SubType.prototype.sayAge=function() { alert(this.age); }; var instance1 = new SubType("dwqs",20); instance1.colors.push("black"); alert(instance1.colors); //red,blue,white,black instance1.sayName(); //dwqs instance1.sayAge(); //20 var instance2 = new SubType("i94web",25); alert(instance2.colors); //red,blue,white instance2.sayName(); //i94web instance2.sayAge(); //25
只调用了一次SuperType()构造函数,并且避免了SuperType.prototype上创建多余和不必要的属性,原型链保持不变,能够使用instanceof和isPrototypeOf()方法。
原文首发:http://www.ido321.com/1381.html