许多 OO 语言(面向对象语言)都支持两种继承方式:接口继承和实现继承。接口继承只继承方法名,而实现继承则继承实际的方法。
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么,我们让原型对象等于另一个类型的实例,
此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针,如此层层递进,就构成了实例与原型的链条。
一种基本的模式:
function SuperType(){ this.prototype = true; } SuperType.prototype.getSuperValue = function(){ return this.prototype; } function SubType(){ this.subprototype = false; } SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function(){ return this.subprototype; } var instance = new SubType(); alert(instance.getSuperValue()); //true
继承是通过创建 SuperType 的实例,并将该实例赋给SubType.prototype 实现的。实现的本质是重写原型对象,代之以一个新类型的实例。
原来存在于 SuperType 的实例中的所有属性和方法,现在也存在于 SubType.prototype 中了,关系图 如下:上面的例子来说,调用
instance.getSuperValue()会经历三个搜索步骤: 1)搜索实例; 2)搜索 SubType.prototype;
3)搜索 SuperType.prototype,最后一步才会找到该方法。在找不到属性或方法的情况下,搜索过程总是要一环一环地前行到原型链末端才会停下来。
默认原型都会包含一个内部指针,指向 Object.prototype。这也正是所有自定义类型都会继承 toString()、valueOf()等默认方法的根本原因。
这是一个完整的原型路径:
使用 instanceof 操作符,只要用这个操作符来测试实例与原型链中出现过的构造函数,结果就会返回 true。
alert(instance instanceof Object) //true
alert(instance instanceof SuperType); //true
alert(instance instanceof SubType) //true
可以说 instance 是 Object、 SuperType 或 SubType 中任何一个类型的实例。
使用 isPrototypeOf()方法。同样,只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型,
alert(Object.prototype.isPrototypeOf(instance)); //true alert(SuperType.prototype.isPrototypeOf(instance)); //true alert(SubType.prototype.isPrototypeOf(instance)); //true
给原型添加方法的代码一定要放在替换原型的语句之后,因为这个是对原型的重写,如果顺序放错了,就出问题了
原型链虽然很强大,可以用它来实现继承,最主要的问题来自包含引用类型值的原型。
function SuperType(){ this.colors = ["red", "blue", "green"]; } function SubType(){ } //继承了 SuperType SubType.prototype = new SuperType(); var instance1 = new SubType(); var instance2 = new SubType(); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" alert(instance2.colors); //"red,blue,green,black"
当intance1做修改时,就影响了instance2
另一个问题:在创建子类型的实例时,不能向超类型的构造函数中传递参数。
function Person(name,age){ this.color = ["red","yellow","blue"]; this.name = name; this.age = age; } function Student(name,age){
//继承了Person Person.apply(this,arguments); Person.call(this,name,age); } var s1 = new Student("Linda",25); var s2 = new Student("John",32); s1.color.push("black"); alert(s1.color+s1.name); //"red","yellow","blue","black" alert(s2.color+s2.name); //"red","yellow","blue"
借用构造函数有一个很大的优势: 即可以在子类型构造函数中向超类型构造函数传递参数。
借用构造函数的问题: 方法都在构造函数中定义,因此函数复用就无从谈起了。
指的是将原型链和借用构造函数的技术组合到一块,使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
function Person(name){ this.name = name; this.firends = ["A","B","C"]; } Person.prototype.sayName = function(){ alert(this.name); } function Student(name,age){ Person.apply(this,[name]); } Student.prototype = new Person(); Student.prototype.constructor = Student; Student.prototype.sayAge = function(){ alert(this.age); } var s1 = new Student("Linda",25); var s2 = new Student("John",32); s1.sayName(); //"Linda" s1.firends.push("D"); alert(s1.firends); //"A","B","C","D" alert(s2.firends); //"A","B","C"
组合继承避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为 JavaScript 中最常用的继承模式。
原文:http://www.cnblogs.com/a-lonely-wolf/p/5605452.html