原型链继承
步骤及关键点
基础步骤:
- 定义父类型构造函数
- 给父类型的原型添加方法
- 定义子类型的构造函数
- 创建父类型的对象赋值给子类型的原型
- 将子类型原型的构造函数属性设给为子类型
- 给子类型原型添加方法
- 创建子类型的对象:可以调用父类型的方法
- 修正constructor属性
关键点:
子类型的原型为父类型的一个实例对象
定义父类和子类构造函数
function Father()
{
this.money = "一个亿!"
}
Father.prototype.showMoney = function(){
console.log("我爸实际有多少钱:"+this.money)
}
function Son()
{
this.name = "江流"
}
这里两个构造函数实际上是没有父子关系,在ES5中是无法直接通过两个函数类型来实现基础的,只是在这里先理解为父类和子类。大概的逻辑图为(地址是瞎写的,实际地址不是这样):

让子类型的原型为父类型的一个实例对象
这里分别给父类型的原型添加方法showMoney()
和子类型的原型添加方法showName()
,继承就是通过子类型的实例去访问父类型的方法,例如让子类实例son1去访问方法showMoney()
,即:son1.showMoney();//报错
,这样直接访问一定是会报错的。
但是如果使用son1
去访问toString()
,却不会报错,其原因就是son1
会通过隐形原型属性__proto__
找到Object
原型对象,然后再其中找到toString()
方法。故此让子类型的原型成为父类型的一个实例对象,就可以让子类型的实例通过隐形原型属性__proto__
访问到父类型的原型中的方法。
故此代码为:
Son.prototype = new Father();
但是这样会有一个问题,如果使用Son.prototyoe.constructor
,会指向一个父类型构造函数,这是一个错误的答案,因为父类型生成的新实例对象中本身不会添加constructor
属性,会通过隐形__proto__
在Father
的原型函数中找到这个constructor
属性,并且给出这个答案,故此我们还需要修正这个实例对象的constructor
属性为子类型的构造函数。
Son.prototype.constructor = Son;
测试代码:
function Father()
{
this.money = "一个亿!"
}
Father.prototype.showMoney = function(){
console.log("我爸实际有多少钱:"+this.money)
}
function Son()
{
this.name = "江流"
}
Son.prototype = new Father();
Son.prototype.constructor = Son;
Son.prototype.showName= function(){
console.log("名字:"+this.name);
}
var son1 = new Son();
son1.showMoney();
son1.showName();

成功通过子类型的实例调用到父类型的方法,并且打印出了,而且不会影响自身的方法。
大概逻辑图是(地址是瞎写的,实际地址不是这样):

特点
优点:
缺点:
- 子类的新实例无法向父类构造函数传参
- 所有新实例都会共享父类实例的属性(原型上的属性是共享的,一个实例修改了原型属性,另一个实例的原型属性也会被修改!)
借用构造函数继承(不是真正的继承)
步骤及关键点
步骤:
- 定义父类型构造函数
- 定义子类型构造函数
- 在子类型构造函数中调用父类型构造
关键点:
在子类型构造函数中用过call()、apply(),bind()
调用父类型构造函数
call()方法
将父类的this指向子类的this,这样就可以实现子类继承父类的属性
定义父类和子类构造函数
从刚才的例子稍作修改
function Father(name,age)
{
this.name = name,
this.age = age
};
function Son(name, age, money)
{
Father.call(this, name, age);
this.money = money
};
var son1 = new Son("心猿", 5000, 150000);
console.log(son1.name, son1.age, son1.money);

特点
优点:
- 解决了原型链继承的部分缺点
- 只继承了父类构造函数的属性,没有继承父类原型的属性
- 可以继承多个构造函数属性(call多个)
- 可以在子实例中向父类实例传参
缺点:
- 只能继承父类构造函数的属性
- 每次用每次都要重新调用
- 每个新实例都会有父类构造函数的副本
组合继承(常用,结合上面两种方式使用)
步骤及关键点
- 原型链 + 借用构造函数的组合继承
- 利用原型链实现对父类型对象的方法继承
- 利用call()借用父类型构建函数初始化相同属性
function Father(name, age,) {
this.name = name;
this.age = age;
}
Father.prototype.showMoney = function () {
console.log("我爸实际有多少钱:" + this.money)
}
function Son(name, age, money) {
Father.call(this, name, age)
this.money = money;
}
Son.prototype = new Father();
Son.prototype.constructor = Son;
Son.prototype.showName = function () {
console.log("我的名字是" + this.name + ",今年我" + this.age + "岁");
}
var son1 = new Son("马少爷",21,"我爸对钱不感兴趣!");
son1.showName();
son1.showMoney();

特点
优点:
- 可以继承父类原型上的属性,可以传参,可复用
- 每个新实例引入的构造函数属性是私有的
缺点:
- 调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数
寄生式继承(常用)
步骤及关键点
关键点:
- 使用
Son.prototype = Object.create(Father.prototype)
来替代Son.prototype = new Father();
实现原理
在上面的原型链继承中,我们通过Son.prototype = new Father();
使子类型的原型成为父类型的一个实例对象,但是如果我们能直接让子类型拿到父类型原型上的方法,就也能得到继承的效果。
而Object.create()
这个方法就能实现这个效果。
所以将Son.prototype = Object.create(Father.prototype)
来替代上面的代码就行了,但是这个Object.create()
方法ES3是不支持的,所以还需要写个备用方案。
if(!Object.create){
Object.create = function(obj){
function F(){};
F.prototype = obj;
return new F();
}
}
故此修改上面代码为:
function Father(name, age,) {
this.name = name;
this.age = age;
}
Father.prototype.showMoney = function () {
console.log("我爸实际有多少钱:" + this.money)
}
function Son(name, age, money) {
Father.call(this, name, age)
this.money = money;
}
if(!Object.create){
Object.create = function(obj){
function F(){};
F.prototype = obj;
return new F();
}
}
Son.prototype = Object.create(Father.prototype);
Son.prototype.constructor = Son;
Son.prototype.showName = function () {
console.log("我的名字是" + this.name + ",今年我" + this.age + "岁");
}
var son1 = new Son("马少爷",21,"我爸对钱不感兴趣!");
son1.showName();
son1.showMoney();

特点
优点:修复了组合继承的问题
ES6中extends继承(常用)
语法:
class Father {
}
class Son extends Father {
}
通过ES6语法重新写上面的例子
class Father {
constructor(name, age) {
this.name = name;
this.age = age;
}
showMoney = function () {
console.log("我爸实际有多少钱:" + this.money)
}
}
class Son extends Father {
constructor(name, age, money) {
super(name, age, money)
this.money = money
}
showName = function () {
console.log("我的名字是" + this.name + ",今年我" + this.age + "岁");
}
}
var son1 = new Son("马少爷", 21, "我爸对钱不感兴趣!");
son1.showName();
son1.showMoney();
