js继承总共分成5种,包括构造函数式继承、原型链式继承、组合式继承、寄生式继承和寄生组合式继承。

构造函数式继承

首先来看第一种,构造函数式继承,顾名思义,也就是利用函数去实现继承;构造函数继承,使用call和apply两个方法可以实现改变方法中的this

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

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

// 子类构造函数
function Son(color) {
Parent.call(this, color);
  • 优点:所有的基本属性独立,不会被其他实例所影响;
  • 缺点:所有希望共享的方法和属性也独立了,没有办法通过修改父类某一处来达到所有子实例同时更新的效果;同时,每次创建子类都会调用父类构造函数一次,所以每个子实例都拷贝了一份父类函数的内容,如果父类很大的话会影响性能;

原型链继承

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的一个特性就是:如果一个对象某个属性找不到,会沿着它的原型往上去寻找,直到原型链的最后才会停止寻找。

回到代码,我们看到最后实例son成功调用了Print方法,输出了color属性,这是因为son从函数Son的prototype属性上面去找到的,也就是从new Parent这个对象里面找到的;

这种方式也不是真正的继承,因为所有的子实例的属性和方法,都在父类同一个实例上了,所以一旦某一个子实例修改了其中的方法,其他所有的子实例都会被影响,来看下代码:

function Flower() {
this.colors = ['黄色', '红色'];
this.print = function () {
console.log(this.colors)
}
}

function Rose() {}
Rose.prototype = new Flower();
Rose.prototype.constructor = Rose;

var r1 = new Rose();
var r2 = new Rose();

console.log(r1.print()); // [ '黄色', '红色' ]
console.log(r1.print()); // [ '黄色', '红色' ]

r1.colors.push('紫色');

console.log(r1.print()); // [ '黄色', '红色', '紫色' ]
console.log(r2.print()); // [ '黄色', '红色', '紫色' ]

还是刚才的例子,这次Rose子类选择了原型链继承,所以,子实例r1修改了colors之后,r2实例的colors也被改动了,这就是原型链继承不好的地方。

来总结下原型链继承的优缺点:

  • 优点:很好的实现了方法的共享;
  • 缺点:正是因为什么都共享了,所以导致一切的属性都是共享的,只要某一个实例进行修改,那么所有的属性都会变化;

组合式继承

​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赋值给父类的实例,这样,所有子类的实例对象就可以共享父类原型上定义的所有属性和方法。这也不难理解,因为子实例会沿着原型链去找到父类函数的原型。

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

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

javaScript继承的几种实现方式?的更多相关文章

  1. js(javascript) 继承的5种实现方式

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt240 js继承有5种实现方式:1.继承第一种方式:对象冒充  functio ...

  2. javascript继承有5种实现方式

    1.对象冒充 function Parent(username){ this.username = username; this.hello = function(){ alert(this.user ...

  3. JS学习笔记——JavaScript继承的6种方法(原型链、借用构造函数、组合、原型式、寄生式、寄生组合式)

    JavaScript继承的6种方法 1,原型链继承 2,借用构造函数继承 3,组合继承(原型+借用构造) 4,原型式继承 5,寄生式继承 6,寄生组合式继承 1.原型链继承. <script t ...

  4. 面向面试编程——javascript对象的几种创建方式

    javascript对象的几种创建方式 总共有以下几个模式: 1.工厂模式 2.构造函数模式 3.原型模式 4.混合构造函数和原型模式 5.动态原型模式 6.寄生构造函数模式 7.稳妥构造函数模式 1 ...

  5. JavaScript脚本的两种放置方式

    JavaScript脚本的两种放置方式 1在body里用 script标签引用 2 直接写在<script></script>标签之中

  6. JavaScript继承的几种实现

    0 什么是继承 继承就是获得存在对象已有的属性和方法的一种方式. [2019.4.26 更新]今日又重新学习了一下JS的继承,在这里整理一下以前的笔记并补充一些新的感悟. 1 JS中继承的几种实现方法 ...

  7. javascript继承的三种模式

    javascript继承一般有三种模式:组合继承,原型式继承和寄生式继承: 1组合继承:javascript最为广泛的继承方式通过原型链实现对原型属性和方法的继承,通过构造函数实现对实例属性的继承,同 ...

  8. JavaScript 函数的两种声明方式

    1.函数声明的方式 JavaScript声明函数有两种选择:函数声明法,表达式定义法. 函数声明法 function sum (num1 ,num2){ return num1+num2 } 表达式定 ...

  9. javascript中对象两种创建方式

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

随机推荐

  1. guitar pro 系列教程(六):Guitar Pro音频导出功能之RSE音源

    让我们继续进行guitar pro的教程 上一章节,我们讲解了guitar Pro的播放与显示功能,在Guita pro的音源选择中分为两类,一种是自带的RES高保真音源,一种是MIDI输入音源.如果 ...

  2. css3系列之linear-gradient() repeating-linear-gradient() 和 radial-gradient() repeating-radial-gradient()

    linear-gradient()  (线性渐变) repeating-linear-gradient()   (重复的线性渐变) radial-gradient()  (镜像渐变) repeatin ...

  3. Python多线程join和setDaemon区别与用法

    一直没有太搞清楚join和setDaemon有什么区别,总是对于它们两个的概念很模糊,需要做个实验然后记录一下. 先说结论: join: 子线程合并到主线程上来的作用,就是当主线程中有子线程join的 ...

  4. jenkins.war的配置

    目录 1.进入root用户-------切换到home下的用户-----然后查看lsx下的文件 2.移动jenkins.war 3.找到刚才移动的文件 4.启动tomcat 5.在浏览器登录 6.进入 ...

  5. Java基础教程——线程池

    启动新线程,需要和操作系统进行交互,成本比较高. 使用线程池可以提高性能-- 线程池会提前创建大量的空闲线程,随时待命执行线程任务.在执行完了一个任务之后,线程会回到空闲状态,等待执行下一个任务.(这 ...

  6. Kubernetes-20:日志聚合分析系统—Loki的搭建与使用

    日志聚合分析系统--Loki 什么是Loki? Loki 是 Grafana Labs 团队最新的开源项目,是一个水平可扩展,高可用性,多租户的日志聚合系统.它的设计非常经济高效且易于操作,因为它不会 ...

  7. 新手上路之JDK8的下载、安装与PATH环境变量的配置

    有些东西不常用总是会忘记,所以想把它写下来,方便以后自己想用的时候找得到:同时也进一步加深自己的记忆.接触JAVA的时间不长,言语或内容有不当之处,欢迎大佬们指正. 每一个学习JAVA的人都会经历的过 ...

  8. 03-Python里字符串的常用操作方法二

    1.lstrip():删除左侧空白字符 实例: my_str = ' hello world and my and test and python ' # 原始字符串 print(my_str) # ...

  9. LeetCode 010 Regular Expression Matching

    题目描述:Regular Expression Matching Implement regular expression matching with support for '.' and '*' ...

  10. java12(eclipse断点调试)

    选择结构switch 1.格式: switch(整型数据){ case 值A:System.out.println("");break; case 值B:System.out.pr ...