OK,有了上一节的基础,我们就可以用ES5来实现一个手写的继承了。

这里继承的实现方法也是面试中比较常见的问题,我们从写一个最简单的模式,然后通过分析问题所在从而进行改善,最终实现一个比较完美的继承。

javascript class继承 js类的继承_父类

1.创建一个父类

我们如果想创建一个父类,用方法的模式来写,给类添加成员变量和方法,最简的就是这样写。

function People(name,age){
    this.name = name;
    this.age = age;
    this.sayHello = function(){
        console.log("Hello");
    }
}

var p1 = new People();
p1.sayHello();

那我们这么写会不会有问题呢?
我们思考一个问题:关于属性和方法对于实例化对象有什么特点?

成员变量是实例化对象各自拥有的不同的属性值。
成员方法是所有实例化对象共有的属性

如果像上面这么写的话,每次我通过构造函数生成一个实例化对象,似乎都要创建这些属性和方法。
属性倒无所谓,每个对象的属性值可能不同。
但是方法确是每个对象都一样的,似乎每次创建对象都要实例化一个方法,似乎有些不妥,我们能不能把这个方法放在一个地方,让每个实例化对象都可以找到它呢?

这就可以用上一节说的原型对象的方法了,我们可以把方法写在原型对象下面,这样这个方法就是所有实例化对象共有的了。

function People(name,age){
    this.name = name;
    this.age = age;
}

People.prototype.sayHello = function(){
    console.log("Hello");
}

var p1 = new People();
p1.sayHello();

所以这里我们记住

属性写在构造函数里面,方法写在原型下面。

2.属性的继承

现在我们创建一个子类Student

function Student(name,age,sex){
    this.sex = sex;
    
}

这个子类有一个自己的属性sex,而name属性和age属性我现在要继承于父类People。

那我们如何来实现呢?

javascript class继承 js类的继承_父类_02


现在我们来回顾一下从前的知识,就是如何能够改变this的指向问题呢?

这里面我们知道可以有call,apply,bind这三个方法可以做到。

那重点来了!!!!!!

现在我们的父类People,本身就是通过函数function来定义的,所以这个Person一定也是可以调用call这样的方法的!

那如果我们在子类中这样写,会起到什么样的效果呢?

function Student(name,age,sex){
    this.sex = sex;
    People.call(this,name,age);
}

这里有一点难理解,我用图解来说明一下:

javascript class继承 js类的继承_类_03


这里面的call方法就用的很巧妙!


通过调用父类的构造方法,给子类添加父类的属性,并且通过call方法来改变this的指向。让this指回子类。

那这样的话,父类的属性我们也就继承过来了。

3.方法的继承

我们知道了父类的方法写在了原型对象下面,那如果我们把子类的原型对象指向父类的原型对象。
这样可不可以呢?

Student.prototype = People.prototype;

似乎乍一看没有问题,父类的方法全部都继承过来,并且父类有增加的方法子类也会跟着同步。
但是如果我们这样。

Student.prototype.eat = function(){
    console.log("im eat");
}

p1.eat()

我们给子类添加新的方法后,父类的实例化对象也可以对这个方法进行调用了。
这并不是我们想要的。
里面的原因是因为父类和子类的Prototype指向了一个原型对象,所以二者的方法会有一个同步的效果,所以我们要进行修改。

如何修改呢?思路就是我们给子类一个新的原型对象,这个原型对象还要有父类原型下的方法。
这里我们可以用一个方法叫Object.create();

这个方法会以指定的原型对象创建一个新的对象

所以我们可以这样:

Student.prototype = Object.create(People.prototype);

我们以父类People的原型对象为参数,创建一个新对象,赋给子类Student的原型对象。
这样子类就有了自己的原型对象,并且和父类互不干扰。

4.修改构造函数

属性和方法的继承我们都写完了,那就没有问题了吗?
不不不,如果看了我上一篇说的,就会知道刚刚方法的继承是有问题的。
因为我们把子类的原型对象修改为了一个新的原型对象,那么子类的原型对象下的构造函数一定也会跟着改变。

那变成了什么呢?
因为我们是以父类的原型对象为参数,创建新对象的,所以子类的构造函数也跟着变成了父类People的构造函数。
所以这里我们要进行修改一下:

Student.prototype.constructor = Student;

那到这里关于ES5的继承机制也就全部都说完了。