一、原型链继承
- js中无法实现接口继承,依靠原型链来实现继承
基本思想:通过改变prototype的指向形成实例和原型之间的原型链
instanceSub->subType.prototype(instanceSuper)->superType.prototype-> Object.prototype->null
- 实现步骤
- 子对象的原型是父对象的实例
subType.prototype = instanceSuper;
或subType.prototype=new superType();
- 父对象的实例上添加 constructor 属性 指向 subType
subType.prototype.constructor = subType;
改变prototype
- 动态修改prototype的属性时,会影响所有已经创建和新创建的实例。
- 重写prototype,即将prototype赋值为新的对象(对象字面量形式)时,不会影响已经创建的实例,会影响后序创建的实例
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
}
function subType() {
this.subproperty = false;
}
var instanceSuper = new SuperType();
subType.prototype = instanceSuper; //子对象的原型是父对象的实例
//SuperType的实例作为 subType的原型
subType.prototype.constructor = subType;//添加 constructor 属性 指向 subType
//子对象的原型设置为父对象的实例后,再向子对象原型添加方法
subType.prototype.getSubValue = function() {
return this.subproperty;
}
var instanceSub = new subType();
//子对象能够继承到父对象的方法
console.log(instanceSub.getSuperValue()); //true
console.log(instanceSub.getSubValue()); //false
原型链的问题
1. 原型链上的属性都是共享的,包含引用类型值(eg:数组类型)的原型属性会被所有实例共享,一改全改
2. 创建子类型的实例时 ,不能向父类型的构造函数中传递参数
3. 解决:使用构造函数解决原型链继承的问题
二、构造函数继承
基本思想:在子对象的构造函数内部调用父类的构造函数
function SubType(type, price) {
SuperType.call(this, type);//调用父类构造方法实现继承,同时可以传参
}
- 实例本身不能继承构造函数,实例可以使用构造函数中的属性和方法是由于this
构造函数实现继承的问题
- 实例继承自构造函数,而构造函数原型上的属性和方法对实例对象不可见.
- 构造函数模式的问题:方法都在构造函数中定义,无法复用
三、原型+构造函数组合继承(常用)
基本思想:使用原型链实现对原型属性和方法的继承,构造函数实现对实例属性的继承
原型+构造函数组合继承的问题: 调用两次超类构造函数。1.创建子类原型 2. 在子类构造内部调用父类构造
function SuperType(name) {
this.name=name;
this.color = ['red', 'green', 'blue'];
}
SuperType.prototype.sayName=function(){
console.log(this.name);
}
function SubType(name,age) {
this.age = age;
SuperType.call(this,name);//调用父类构造方法实现继承.
}
//继承构造函数原型上的属性和方法
SubType.prototype=new SuperType();//调用父类构造函数
SubType.prototype.constructor=SubType;
SubType.prototype.sayAge=function(){
console.log(this.age);
}
var sub = new SubType('lily',18);
sub.color.push('black');
console.log(sub.color);//["red", "green", "blue", "black"]
var sub2 = new SubType('nacy',12);
console.log(sub2.color);//["red", "green", "blue"]
四、原型式继承
基本思想:创建一个生成临时构造函数的函数,借助原型,基于已有的对象创建新对象
function object(o){
function F(){};//创建临时构造函数
F.prototype=o;//将临时构造函数的原型设置为超类的对象.
return new F();//返回临时构造函数的新实例
}
var person2=object(person);//则 person2 的原型为person
-
Object.create() 方法规范化了原型式继承,可以使用Object.create()替换object函数
-
Object.create() 是一个系统内置的函数,接收一个参数,一般是对象,返回新创建的对象,并且对象的原型指向传入的参数。
创建一个空对象,空对象的原型,是传入的参数。 -
Object.create(null); null没有原型,没有prototype属性 ,所以没有toString方法
-
使用bind函数将函数指向为null后,该函数也没有prototype属性。
原型式继承的问题
- 引用类型值的属性会被所有实例共享
五、寄生式继承
基本思想:原型式继承+工厂模式(创建一个仅用于封装继承过程的函数,该函数在内部为对象添加属性和方法,然后返回对象)
寄生式继承的问题:不能做到函数复用
function object(o){
function F(){};
F.prototype=o;//将临时构造函数的原型设置为超类的对象.
return new F();
}
function createAnother(original){
var clone=object(original);
clone.sayHi=function(){//添加函数后返回该对象,函数不共享
console.log('hi');
}
return clone;
}
var person={
name:'张三',
friends:['lily','nacy','tom']
}
var person2=createAnother(person);
person2.sayHi();
六、寄生组合式继承(引用类型的理想继承)
基本思想: 寄生式继承+组合继承
- 构造函数 继承 属性;原型链混成形式 继承 方法
function object(o){
function F(){};
F.prototype=o;//将临时构造函数的原型设置为超类的对象.
return new F();
}
//创建超类构造函数的副本,赋值给子类型的原型
function inheritPrototype(subType,superType){//两个参数:子类构造函数,父类构造函数
var prototype=object(superType.prototype);//创建超类型原型的副本
prototype.constructor=subType;
subType.prototype=prototype;//将新创建的对象赋值给子类型的原型
}