OK,有了上一节的基础,我们就可以用ES5来实现一个手写的继承了。
这里继承的实现方法也是面试中比较常见的问题,我们从写一个最简单的模式,然后通过分析问题所在从而进行改善,最终实现一个比较完美的继承。
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。
那我们如何来实现呢?
现在我们来回顾一下从前的知识,就是如何能够改变this的指向问题呢?
这里面我们知道可以有call,apply,bind这三个方法可以做到。
那重点来了!!!!!!
现在我们的父类People,本身就是通过函数function来定义的,所以这个Person一定也是可以调用call这样的方法的!
那如果我们在子类中这样写,会起到什么样的效果呢?
function Student(name,age,sex){
this.sex = sex;
People.call(this,name,age);
}
这里有一点难理解,我用图解来说明一下:
这里面的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的继承机制也就全部都说完了。