动力节点首页 全国咨询热线:400-8080-105

绑定手机号,登录
手机号

验证码

微信登录
手机号登录
手机号

验证码

微信登录与注册
微信扫码登录与注册

扫码关注微信公众号完成登录与注册
手机号登录
首页 > 文章

深入理解5种JavaScript继承方式

04-29 17:34 543浏览
举报 T字号
  • 大字
  • 中字
  • 小字

继承是很多编程语言的特性之一,JavaScript脚本语言同样具备这一特性。JavaScript继承总共分成5种,包括构造函数式继承、原型链式继承、组合式继承、寄生式继承和寄生组合式继承,下面我们来一一介绍。

1.构造函数式继承

首先来看第一种,构造函数式继承,顾名思义,也就是利用函数去实现继承;

假设我们现在有一个父类函数:

// 父类构造函数
function Parent(color) {
    this.color = color;
    this.print = function() {
        console.log(this.color);
    }
}

现在要编写一个子类函数来继承这个父类,如下:

// 子类构造函数
function Son(color) {
    Parent.call(this, color);
}

上面代码可以看到,子类Son是通过Parent.call的方式去调用父类构造函数,然后把this对象传进去,执行父类构造函数之后,子类Son就拥有了父类定义的color和print方法。调用一下该方法,输出如下:

// 测试
var son1 = new Son('red');
son1.print(); // red
​
var son2 = new Son('blue');
son2.print(); // blue

可以看到son1和son2都正常继承了父类的print方法和各自传进去的color属性;

以上就是构造函数式继承的实现了,这是最原始的js实现继承的方式;

2.原型链继承

下面我们来看第二种继承方式,原型链式继承;

同样先来看下例子:

function Parent() {
    this.color = 'red';
    this.print = function() {
        console.log(this.color);
    }
}
function Son() {
}

我们有一个父类和一个空的子类;

Son.prototype = new Parent();
Son.prototype.constructor = Son;

接着我们把子函数的原型属性赋值给了父函数的实例;

var son1 = new Son();
son1.print(); // red

最后新建子类实例,调用父类的方法,成功拿到父类的color和print属性方法;

我们重点来分析一下下面两行代码:

Son.prototype = new Parent();
Son.prototype.constructor = Son;

这段代码中,子函数的原型赋给了父函数的实例,我们知道prototype是函数中的一个属性,js的一个特性就是:如果一个对象某个属性找不到,会沿着它的原型往上去寻找,直到原型链的最后才会停止寻找。

3.组合式继承

这里来介绍第三种继承方式,组合式继承;

这种继承方式很好理解,既然构造函数式继承和原型链继承都有各自的优缺点,那么我们把它们各自的优点整合起来,不就完美了吗?

组合式继承做的就是这个事情~来看一段代码例子:

​function Parent(color) {
    this.color = color;
}
Parent.prototype.print = function() {
    console.log(this.color);
}
function Son(color) {
    Parent.call(this, color);
}
Son.prototype = new Parent();
Son.prototype.constructor = Son;
​
var son1 = new Son('red');
son1.print(); // red
​
var son2 = new Son('blue');
son2.print(); // blue

上面代码中,在Son子类中,使用了Parent.call来调用父类构造函数,同时又将Son.prototype赋给了父类实例;为什么要这样做呢?为什么这样就能解决上面两种继承的问题呢?

我们接着分析一下,使用Parent.call调用了父类构造函数之后,那么,以后所有通过new Son创建出来的实例,就单独拷贝了一份父类构造函数里面定义的属性和方法,这是前面构造函数继承所提到的一样的原理;

然后,再把子类原型prototype赋值给父类的实例,这样,所有子类的实例对象就可以共享父类原型上定义的所有属性和方法。这也不难理解,因为子实例会沿着原型链去找到父类函数的原型。

因此,只要我们定义父类函数的时候,将私有属性和方法放在构造函数里面,将共享属性和方法放在原型上,就能让子类使用了。

以上就是组合式继承,它很好的融合了构造函数继承和原型链继承,发挥两者的优势之处,因此,它算是真正意义上的继承方式。

4.寄生式继承

既然上面的组合式继承都已经这么完美了,为什么还需要其他的继承方式呢?

我们细想一下,Son.prototype = new Parent();这行代码,它有什么问题没有?

显然,每次我们实例化子类的时候,都需要调用一次父类构造函数,那么,如果父类构造函数是一个很大很长的函数,那么每次实例化子类就会执行很长时间。

实际上我们并不需要重新执行父类函数,我们只是想要继承父类的原型。

寄生式继承就是在做这个事情,它是基于原型链式继承的改良版:

var obj = {
    color: 'red',
    print: function() {
        console.log(this.color);
    }
};
​
var son1 = Object.create(obj);
son1.print(); // red
​
var son2 = Object.create(obj);
son2.print(); // red

寄生式继承本质上还是原型链继承,Object.create(obj);方法意思是以obj为原型构造对象,所以寄生式继承不需要构造函数,但是同样有着原型链继承的优缺点,也就是它把所有的属性和方法都共享了。

5.寄生组合式继承

接下来到我们最后一个继承方式,也就是目前业界最为完美的继承解决方案:寄生组合式继承。

没错,它就是es6的class语法实现原理。

但是如果你理解了组合式继承,那么理解这个方式也很简单,只要记住,它出现的主要目的,是为了解决组合式继承中每次都需要new Parent导致的执行多一次父类构造函数的缺点。

下面来看代码:

function Parent(color) {
    this.color = color;
}
Parent.prototype.print = function() {
    console.log(this.color);
}
function Son(color) {
    Parent.call(this, color);
}
Son.prototype = Object.create(Parent.prototype);
Son.prototype.constructor = Son;
​
var son1 = new Son('red');
son1.print(); // red
​
var son2 = new Son('blue');
son2.print(); // blue

这段代码不同之处只有一个,就是把原来的Son.prototype = new Parent();修改为了Son.prototype = Object.create(Parent.prototype);

我们前面讲过,Object.create方法是以传入的对象为原型,创建一个新对象;创建了这个新对象之后,又赋值给了Son.prototype,因此Son的原型最终指向的其实就是父类的原型对象,和new Parent是一样的效果;

以上就是5种JavaScript继承方式,在我们的动力节点在线网站的JavaScript视频教程中都有详细的讲解,通俗易懂,让我们能够快速掌握其中的关键知识,想学习的小伙伴抓紧时间开始学习吧!

0人推荐
共同学习,写下你的评论
0条评论
杨晶珍
程序员杨晶珍

98篇文章贡献357785字

相关课程 更多>

作者相关文章更多>

推荐相关文章更多>

JavaWeb的3大组件

代码小兵49806-11 15:28

全面解析Cookie技术

代码小兵49806-11 15:51

浅谈JavaWeb架构演变

代码小兵49806-11 16:22

探讨Web开发中的Session存储与管理

代码小兵51603-29 17:28

JavaScript基础知识

 暴风城-小飞04-06 20:49

发评论

举报

0/150

取消