每个类有3部分,
第一部分是构造函数内的,供实例化对象复制用的
第二部分是构造函数外的,直接通过点语法添加,这是供类使用的,实例化对象访问不到
第三部分是类的原型中,实例化对象可以通过其原型链间接访问到,也是供所有实例化对象共用的。
1.类式继承
//类式继承
//声明父类
function SuperClass(){
this.superValue=true;
}
//为父类添加共有方法
SuperClass.prototype.getSuperValue=function(){
return this.superValue;
};
//声明子类
function SubClass(){
this.subValue=false;
}
//继承父类
SubClass.prototype=new SuperClass();//子类的原型为父类的实例
//为子类添加共有方法
SubClass.prototype.getSubValue=function(){
return this.subValue;
};
类的原型对象是为类的原型添加共有方法,但类不能直接访问这些属性和方法,必须通过原型prototype来访问。通过改变原型,新创建的对象不仅可以访问父类原型的属性和方法,也可以访问从父类构造函数中复制的属性和方法。
var instance=new SubClass();
console.log(instance.getSuperValue());//true
console.log(instance.getSubValue());//false
instanceof是通过判断对象的prototype链来来检测某个对象是否是某个类的实例,而不关心对象和类的自身结构。
console.log(instance instanceof SuperClass);//true
console.log(instance instanceof SubClass);//true
console.log(SubClass instance SuperClass);//false
subclass继承superclass是通过将superclass的实例赋值给subclass的原型prototype,所以SubClass.prototype继承了superClass
console.log(SubClass.prototype instanceof SuperClass);//true
类式继承的缺点:
(1)子类通过原型继承了父类,父类中的共有属性要是引用类型,就会在子类中被所有实例共用。因此一个子类的实例更改子类原型从父类构造函数中继承来的共有属性就会直接影响其他子类。
function SuperClass(){
this.books=['js','html','css'];
}
function SubClass(){}
SubClass.prototype=new SuperClass();
var instance1=new SubClass();
var instance2=new SubClass();
console.log(instance2.books);// js html css
instance1.books.push('设计模式');
console.log(instance2.books);// js html css 设计模式
(2)子类实现的继承是靠其原型prototype对父类的实例化实现的,因此在创建父类的时候,是无法向父类传递参数的,因而在实例化父类的时候也无法对父类构造函数内的属性初始化
2.构造函数继承
//声明父类
function SuperClass(id){
//引用类型共有属性
this.books=['js','html','css'];
//值类型共有属性
this.id=id;
}
//父类声明原型方法
SuperClass.prototype.showBooks=function(){
console.log(this.books);
}
//声明子类
function SubClass(id){
//继承父类
SuperClass.call(this,id);
}
//创建第一个子类的实例
var instance1=new SubClass(10);
//创建第二个子类的实例
var instance2=new SubClass(11);
instance1.books.push('设计模式');
console.log(instance1.books);//js html css 设计模式
console.log(instance1.id);//10
console.log(instance2.books);// js html css
console.log(instance2.id)//11
isntance1.showBooks();//TypeError
由于更改了函数的作用环境,因此在子类中,对superclass调用这个方法就是将子类中的变量在父类中执行一遍,由于父类中是给this绑定属性的,因此子类自然也就继承了父类的共有属性。
由于继承时没有涉及原型prototype,所以父类原型的方法不会被继承。要想被子类继承必须放在构造函数中,这样创建出来的每个实例都会单独拥有一份而不能共用,这就违背了代码复用的原则。
3.组合继承
类式继承是通过子类的原型prototype对父类实例化实现的
构造函数式继承是通过在子类的构造函数作用环境中执行一次父类的构造函数来实现的
//声明父类
function SuperClass(name){
//值类型共有属性
this.name=name;
//引用类型共有属性
this.books=['html','css','js'];
}
//父类原型共有方法
SuperClass.prototype.getName=function(){
console.log(this.name);
};
//声明子类
function SubClass(name,time){
//构造函数式继承父类name属性
SuperClass.call(this,name);
//子类中新增的共有属性
this.time=time;
}
//类式继承
SubClass.prototype=new SuperClass();
//子类原型方法
SubClass.prototype.getTime=function(){
console.log(this.name);
};
这样,子类的实例中更改父类继承下来的引用类型属性如books,根本不会影响其他实例,并且在子类实例化过程中又能将参数传递到父类的构造函数中name。
var instance1=new SubClass('js',2014);
instance1.books.push('设计模式');
console.log(instance1.books); //html css js 设计模式
instance1.getName();//js
isntance1.getTime();//2014
var instance2=new SubClass('css',2013);
console.log(instance2.books);//html css js
instance2.getName();//css
instance2.getTime();//2013
4.原型式继承
//原型式继承
function inheritObject(o){
//声明一个过渡函数对象
function F(){}
//过渡对象的原型继承父对象
F.prototype=o;
//返回过渡对象的一个实例,该实例的原型继承了父对象
return new F();
}
对类式继承的一个封装,由于F过渡类的构造函数中无内容,所以开销较小,使用起来也比较方便。跟类式继承一样,父类对象的book中的值类型属性被复制,引用类型的属性被共用
var book={
name:"js book",
alikeBook:["css","html"]
};
var newBook=inheritObject(book);
newBook.name="ajax";
newBook.alikeBook.push("xml");
var otherBook=inheritObject(book);
otherBook.name="flash";
otherBook.alikeBook.push("as book");
console.log(newBook.name);//ajax
console.log(newBook.alikeBook);//css html xml
console.log(otherBook.name);//flash
console.log(otherBook.alikeBook);// css html xml as book
console.log(book.name);//js book
console.log(book.alikeBook); //css html xml as book
5.寄生式继承
//声明基对象
var book={
name:"js",
alikeBook:["css","html"]
};
function createBook(obj){
//通过原型继承方式创建新对象
var o=new inheritObject(obj);
//拓展新对象
o.getName=function(){
console.log(name);
};
//返回拓展后的新对象
return o;
}
寄生式继承是对原型继承的第二次封装,并且在第二次封装的过程中对继承的对象进行了拓展,这样先创建的对象不仅仅有父类的属性和方法而且添加了新的属性和方法。
6.寄生组合式继承
function inheritPrototype(subClass,superClass){
//复制一份父类的原型副本保存在变量中
var p=inheritObject(superClass.prototype);
//修正因为重写子类原型导致子类的constructor属性被修改
p.constructor=subClass;
//设置子类的原型
subClass.prototype=p;
}