1、类式继承 2、构造函数继承 3、组合继承 4、寄生组合式继承 5、extends继承
(1)类式继承
//声明父类
function Animal() {
this.name = "animal";
this.type = ['pig','cat'];
}
//为父类添加共有的方法
Animal.prototype.greet = function(sound) {
console.log(sound);
}
//声明子类
function Dog() {
this.name = "dog";
}//继承父类
Dog.prototype = new Animal();var dog = new Dog();
console.log(dog.name);
console.log(dog.type);
dog.greet('汪汪汪');
原理说明:在实例化一个类时,新创建的对象复制了父类的构造函数内的属性与方法并且将原型__proto__指向了父类的原型对象,这样就拥有了父类的原型对象上的属性
与方法。
通过类式继承方式,有两个缺点:(1)引用缺陷:通过上面的执行结果,我们看到当通过dog实例对象修改继承自Animal中的数组type(引用类型)时,另外一个新创建的实例
dog2也会受到影响。(2)无法为不同的实例初始化继承来的属性
(2)构造函数继承
构造函数继承方式可以避免类式继承的缺陷:
//声明父类
function Animal(color){
this.name = "animal";
this.type = ['pig','cat'];
this.color = color;
}
//为父类添加共有的方法
Animal.prototype.greet = function(sound) {
console.log(sound);
}
//声明子类
function Dog(color) {
//构造函数继承
Animal.apply(this,arguments);
}var dog = new Dog("白色");
var dog2 = new Dog("黑色");
dog.type.push('tiger');
console.log(dog.type);
console.log(dog2.type);我们在Dog子类中调用这个方法也就是将Dog子类的变量在父类中执行一遍,这样子类就拥有了父类中的共有属性和方法。构造函数继承也是有缺陷的,那就是我们无法获
取到父类的共有方法,也就是通过原型prototype绑定的方法。
(3)组合继承
//声明父类
function Animal(color){
this.color = color;
this.type = ['dog','pig'];
this.name = 'animal';
}
//为父类添加共有的方法
Animal.prototype.greet = function(sound){
console.log(sound);
}
//声明子类
function Dog(color){
//构造函数继承
Animal.apply(this,arguments);
}
//类式继承
Dog.prototype = new Animal();
var dog1 = new Dog('白色');
var dog2 = new Dog('黑色');console.log(dog1.color);
console.log(dog2.color);
dog1.type.push('tiger');
console.log(dog1.type);
console.log(dog2.type);
在上面的例子中,我们在子类构造函数中执行父类构造函数,在子类原型上实例化父类,这就是组合继承了,可以看到它综合了类式继承和构造函数继承的优点,同时去除
了缺陷。这种组合继承也是有点小缺陷的,那就是它调用了两次父类的构造函数。
(4)寄生组合式继承
寄生组合式继承强化的部分就是在组合继承的基础上减少一次多余的调用父类的构造函数:
function Animal(color){
this.color = color;
this.name = 'animal';
this.type = ['dog','cat'];
}
Animal.prototype.greet = function(sound){
console.log(sound);
}
function Dog(){
Animal.apply(this,arguments);
this.name = 'dog';
}
//注意下面两行
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.getName = function(){
console.log(this.name);
}
var dog1 = new Dog();
var dog2 = new Dog();
dog1.type.push('tiger');
console.log(dog1.type);
console.log(dog2.type);
dog1.greet('汪汪');
在上面的例子中,我们并不像构造函数继承一样直接将父类Animal的一个实例赋值给Dog.prototype,而是使用Object.create()进行一次浅拷贝,将父类原型上的方法拷贝
后赋给Dog.prototype,这样子类上就能拥有了父类的共有方法,而且少了一次调用父类的构造函数。
Object.create()的浅拷贝的作用类式下面的函数:
function create(obj) {
function F() {};
F.prototype = obj;
return new F();
}
这里还需注意一点,由于对Animal的原型进行了拷贝后赋给Dog.prototype,因此Dog.prototype上的constructor属性也被重写了,所以我们要修复这一个问题:
Dog.prototype.constructor = Dog;
(5)extends继承
Class和extends是在ES6中新增的,Class用来创建一个类,extends用来实现继承:
class Animal {
constructor(color) {
this.color = color;
}
greet(sound){
console.log(sound);
}
}
class Dog extends Animal {
constructor(color) {
super(color);
this.color = color;
}
}
let dog = new Dog('黑色');
dog.greet('汪汪');
console.log(dog.color);
在上面的代码中,创建了父类Animal,然后Dog子类继承父类,两个类中都有一个constructor构造方法,实质就是构造函数Animal和Dog。不知道你有没有注意到一点,我在子类的构造方法中调用了super方法,它表示父类的构造函数,用来新建父类的this对象。
注意:子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。