ES5继承

1. 原型链继承

function Zoo() {
    this.zoo = '动物园';
}

function panda(name) {
    this.name = name;
}

panda.prototype = new Zoo();

var animal = new panda('熊猫');
console.log(animal.zoo + '里面有一只' + animal.name);

缺点: 不能向父类构造函数(Zoo)传递参数

2. 构造函数继承

——在子类型构造函数的内部调用父类构造函数,使用apply()call()方法将this指向子类

function Zoo(zoo) {
    this.zoo = zoo;
}

function panda(zoo, name) {
    Zoo.call(this,zoo); // 把 Zoo的this 指向panda
    this.name = name;
}

var animal = new panda('动物园','熊猫');
console.log(animal.zoo + '里面有一只' + animal.name);

缺点: 无法调用父类原型上的方法 例如:

Zoo.prototype.sleep = function() {
    console.log('要睡觉觉了!');
}

console.log(animal.sleep()); // TypeError: animal.sleep is not a function

3. 组合继承

——将原型链继承和构造函数继承组合到一起。使用原型链继承实现对原型属性和方法的继承,用借用构造函数继承实现对父实例属性的继承。这样既通过在原型上定义方法实现了函数复用,又能保证每个实例都有自己的属性

function Zoo(zoo) {
    this.zoo = zoo;
}

Zoo.prototype.sleep = function() {
    console.log('要睡觉觉了!');
}

function panda(zoo, name) {
    Zoo.call(this,zoo); // 把 Zoo的this 指向panda
    this.name = name;
}

panda.prototype = new Zoo();
var animal = new panda('动物园','熊猫');
console.log(animal.zoo + '里面有一只' + animal.name);
animal.sleep()

缺点: 会调用两次超类型构造函数,一次是在创建子类型原型的时候,一次是在子类型构造函数的内部,占用内存。

4. 寄生组合式继承

——寄生组合式继承是对组合继承的进一步优化。我们先看一下为什么要写这个语句。

panda.prototype = new Zoo();

我们无非是想让panda继承Zoo的原型。但是我们为什么不直接写成这样呢?

panda.prototype = Zoo.prototype

——这样写确实可以实现子类对象对父类对象原型的继承。但是这样写的话:所有继承该父类的子类对象的原型都指向同一个了。也就是说SubType不能有自己的原型了。这显然不是我们想要的。

——既然不能直接继承,那可不可以间接继承Zoo.prototype呢。这就是最终的解决方案:间接组合式继承。

——我们让一个对象去指向Zoo.prototype,然后让panda.prototype指向这个函数产生的对象不就可以了嘛。
——核心函数:

 function indirect(son, father) {
    var target = Object.create(father.prototype); // 也可以直接写var target = father.prototype; father.prototype本身就是一个对象,万物皆对象
    target.constructor = son;
    son.prototype = target;
}

function Zoo(zoo) {
    this.zoo = zoo;
}

Zoo.prototype.sleep = function() {
    console.log('要睡觉觉了!');
}

function panda(zoo, name) {
    Zoo.call(this,zoo); // 把 Zoo的this 指向panda
    this.name = name;
}

indirect(panda,Zoo);
var animal = new panda('动物园','熊猫');
console.log(animal.zoo + '里面有一只' + animal.name);
animal.sleep()

优点: 只调用了一次Zoo构造函数,因此避免在panda.prototype上创建不必要的,多余的属性,与此同时,原型链还能保持不变,还能正常使用instanceof 和isPrototypeOf(),因此,寄生组合式继承被认为是引用类型最理想的继承范式。

ES6 继承

class Zoo {
    constructor(zoo) {
        this.zoo = zoo;
    }
    sleep() {
        return '要睡觉觉了!';
    }
}

class animal extends Zoo {
    constructor(zoo,age) {
        super(zoo);
        this.age = age;
    }
    eat() {
        console.log('吃完饭饭'+super.sleep()); // 这里的super指代的是父亲构造函数 Zoo 的原型上的方法 Zoo.prototype
    }
}

var panda = new animal('上野动物园','5');

console.log(panda.zoo);// 上野动物园
panda.eat();// 吃完饭饭要睡觉觉了!

——Class 可以通过extends关键字实现继承
——子类必须在constructor方法中调用super方法,否则新建实例时会报错。
——这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。
—— super(zoo)里的参数zoo 是父亲的属性 要是用父类属性需要加上参数