这里先说基于原型链实现的继承。那首先就得明白什么是原型链了;函数
每一个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。this
那么,假如咱们让原型对象等于另外一个类型的实例,此时的原型对象将包含指向另外一个原型对象的指针,相应地,另外一个原型对象也包含着指向另外一个构造函数的指针。spa
假如另外一个原型又是另外一个类型的实例,那么上述关系依然成立。层层递进,就成一条链了。prototype
实现原型链的基本模式:指针
function SuperType() {对象
this.property = true;继承
}原型链
SuperType.prototype.getSuperValue = function() {get
return this.property;原型
}
function SubType() {
this.subproperty = false;
}
SubType.prototype = new SuperType(); //实现继承
SubType.prototype.getSubValue = function() {
return this.subproperty;
}
var instance = new SubType();
alert(instance.getSuperValue); // true
上述SubType经过重写原型对象的方式继承了SuperType,因此原来位于SuperType里的方法,如今也位于SubType里了,咱们没有使用SubType默认提供的原型,而是给它换了一个新原型。这个新原型就是SuperType的实例。
因而,新原型不只有做为SuperType实例全部的属性和方法,内部还有一个指针,指向了SuperType的原型。
从而,instance指向SubType的原型,SubType的原型又指向SuperType的原型;getSuperValue()的方法仍是在SuperType.prototype中,但property则位于SubType.prototype中,由于property是一个实例属性,而getSuperValue()则是一个原型方法。既然SubType.prototype如今是SuperType的实例,那么property固然位于该实例中了
此外,instance.constructor如今指向的是SuperType了,由于原来SubType.prototype指向了SuperType的原型,而这个原型对象的constructor属性指向的是SuperType
默认的原型
咱们知道,全部的引用类型默认都继承了Object,而这个继承也是原型链实现的。全部的函数的默认原型都是Object的实例,所以默认原型里面都会包含一个指针,指向Object.prototype
这也是全部自定义类型都会继承toString(),valueof()等默认方法的根本缘由
原型和实例的关系
alert(isntance instanceof Object) ; // true
alert(isntance instanceof SuperType); // true
alert(isntance instanceof SubType) ; // true
alert(Object.prototype.isPrototypeOf(instance)); // true
alert(SuperType.prototype.isPrototypeOf(instance)); // true
alert(SubType.prototype.isPrototypeOf(instance)); // true
经过原型链实现继承,不能使用字面量对象建立原型方法,那样会重写原型链,切断原来原型与实例之间的联系
原型链存在的问题:
(1)最主要的问题是来自包含引用类型值的缘由
举例:
function SuperType() {
this.colors = ['red', 'yellow', 'blue'];
}
function SubType() {
}
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push('black');
var instance2 = new SubType();
alert(instance1.colors); // 'red,yellow,blue,black'
alert(instance2.colors); // 'red,yellow,blue,black'
SuperType的每一个实例都会包含各自的colors属性,当SubType经过原型链继承以后,SubType.prototype也包含了一个colors属性,为全部实例所共享,全部修改instance1,会反映到instance2
(2)另外一个问题:不能向超类型的构造函数中传递参数
解决办法:
1.借用构造函数(经典继承)
function SuperType () {
this.colors = ['red', 'blue', 'green'];
}
function SubType() {
SuperType.call(this)
}
var instance1 = new SubType();
instance1.colors.push('black');
alert(instance1.colors); // "red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors) // "red,blue,green"
代码红色部分借调了超类型的构造函数,经过使用call()方法,咱们其实是在(将来将要)新建立的SubType实例的环境下调用了SuperType构造函数。就会在新SubType对象上执行SuperType函数中定义的全部对象初始化代码
优点: 传递参数,能够在子类型构造函数中向超类型构造函数传递参数
function SuperType (name) {
this.name = name;
}
function SubType () {
SuperType.call (this, 'bob'); //继承SuperType,同时传递参数
this.age = 29; //实例属性
}
var instance = new SubType();
alert(instance.name); // 'bob'
alert(instance.age); //29
问题:方法都在构造函数中定义,没法复用。并且,在超类型原型中定义的方法,对子类型不可见
2.组合继承
思路:使用原型链实现对原型属性和方法的继承,而经过借用构造函数来实现对实例属性的继承
function SuperType(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
SuperType.prototype.sayName = function() {
alert(this.name);
};
function SubType (name, age) {
SuperType.call(this, name); //继承属性
this.age = age;
}
// 继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function () {
alert(this.age);
};
var instance1 = new SubType('bob', 20);
instance1.colors.push('black');
alert(instance1.colors); // "red,blue,green,black"
instance1.sayName(); // 'bob'
instance1.sayAge(); // 20
var instance2 = new SubType('alice', 21);
alert(instance2.colors); // "red,blue,green"
instance2.sayName(); // 'alice'
instance2.sayAge(); // 21
3.原型式继承(object.create())
实现原理
function object(o) {
function F(){}
F.prototype = o;
return new F() //返回的是构造函数的实例,因此只有_proto_属性,指向F.prototype
}
举例:
var person = {
name: 'bob',
friends: ['a', 'b', 'c']
}
var anotherPerson = Object.create(person);
anotherPerson.name = 'alice';
anotherPerson.friends.push('d');
var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = 'Linda';
yetAnotherPerson.friends.push('e');
alert(person.friends); // 'a,b,c,d,e'
在没有必要兴师动众地建立构造函数,而只想让一个对象与一个对象保持相似的状况下,能够用原型式继承,不过,
包含引用类型的值的属性始终都要共享相应的值,就像使用原型模型同样
4.寄生式继承
function createAnother (original) {
var clone = object.create(original); // 经过调用函数建立一个新对象
clone.sayHi = function() { // 以某种方式加强对象
alert('Hi');
}
return clone; //返回这个对象
}
var person = {
name: 'bob',
friends: ['a', 'b', 'c']
}
var anotherPerson = createAnother(person);
anotherPerson对象具备了person全部属性和方法, 因为采用object.create()建立,因此相似于原型式继承
5.寄生组合式继承
组合式继承最大的不足是不管什么状况下,会调用两次超类型构造函数;一次是在建立子类型原型的时候,另外一次是在子类型构造函数内部。
看下面例子:
function SuperType(name) {
this.name = name;
this.colors = ['red', 'blue', 'green']
}
SuperType.prototype.sayName = function() {
alert(this.name)
}
function SubType() {
SuperType.call(this,name); //第二次调用SuperType()
this.age = age;
}
SubType.prototype = new SuperType(); //第一次调用SuperType()
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function() {
alert(this.age);
}
解释: 第一次调用SuperType()时,将SuperType()的实例属性赋给了SubType.prototype;至关于在SubType.prototype上建立了两个属性name,colors
第二次调用SubType()构造函数时,又会调用SuperType()一次,此次又在新对象上建立了实例属性name和colors,因而,这两个属性就屏蔽了原型中的两个同名属性。这样就在SubType.prototype上建立了多余的没必要要的属性
寄生组合式继承:没必要为了指定子类型的原型而调用超类型构造函数,咱们所须要的无非就是超类型原型的一个副本而已。
本质上,就是利用寄生式继承来继承超类型的原型,再将结果指定给子类型原型
function inheritPrototype(subType, superType) {
var prototype = object.create(superType.prototype); //建立对象
prototype.constructor = subType; // 加强对象
subType.prototype = prototype; //指定对象
}
因此寄生式组合继承:
function SuperType(name) {
this.name = name;
this.colors = ['red', 'blue', 'green']
}
SuperType.prototype.sayName = function() {
alert(this.name)
}
function SubType() {
SuperType.call(this,name);
this.age = age;
}
inheritPrototype(subType, superType);
SubType.prototype.sayAge = function() {
alert(this.age);
}