这篇文章主要介绍JavaScript实现继承的方式:

类式继承
构造函数继承
组合继承
寄生组合式继承
extends继承
1、类式继承

简单的类式继承:

// 声明父类function Animal() {this.name = 'animal';this.type = ['pig', 'cat'];}// 为父类添加共有方法Animal.prototype.greet = function(sound) {console.log(sound);}// 声明子类function Dog() {this.name = 'dog';}// 继承父类Dog.prototype = new Animal();var dog = new Dog();dog.greet('汪汪');  //  "汪汪"console.log(dog.type); // ["pig", "cat"]复制代码

在上面的代码中,我们创建了两个类Animal和Dog,而且给Animal.prototype原型上添加了一个greet共有方法,然后通过new命令实例化一个Animal,并且赋值给Dog.prototype原型。

原理说明:在实例化一个类时,新创建的对象复制了父类的构造函数内的属性与方法并且将原型proto指向了父类的原型对象,这样就拥有了父类的原型对象上的属性与方法。

不过,通过类式继承方式,有两个缺点。

第一个是引用缺陷:


dog.type.push('dog');var dog2 = new Dog();console.log(dog2.type);  // ["dog", "cat", "dog"]复制代码

通过上面的执行结果,我们看到当通过dog实例对象修改继承自Animal中的数组type(引用类型)时,另外一个新创建的实例dog2也会受到影响。

第二个是我们无法为不同的实例初始化继承来的属性,我们可以修改一下上面的例子:


function Animal(color) {this.color = color;}...Dog.prototype = new Animal('白色');...console.log(dog.color); // "白色"console.log(do2.color); // "白色"复制代码

通过上面的代码可以看到,我们无法为不同dog赋值不同的颜色,所有dog只能同一种颜色。

2、构造函数继承

构造函数继承方式可以避免类式继承的缺陷:


// 声明父类function Animal(color) {this.name = 'animal';this.type = ['pig','cat'];this.color = color;}// 添加共有方法Animal.prototype.greet = function(sound) {console.log(sound);}// 声明子类function Dog(color) {Animal.apply(this, arguments);}var dog = new Dog('白色');var dog2 = new Dog('黑色');dog.type.push('dog');console.log(dog.color);  // "白色"console.log(dog.type);  // ["pig", "cat", "dog"]console.log(dog2.type);  // ["pig", "cat"]console.log(dog2.color);  // "黑色"复制代码

首先要知道apply方法的运用,它是可以更改函数的作用域,所以在上面的例子中,我们在Dog子类中调用这个方法也就是将Dog子类的变量在父类中执行一遍,这样子类就拥有了父类中的共有属性和方法。

相关文章:JS中的call、apply、bind方法

但是,构造函数继承也是有缺陷的,那就是我们无法获取到父类的共有方法,也就是通过原型prototype绑定的方法:


dog.greet();  // Uncaught TypeError: dog.greet is not a function复制代码

3、组合继承

组合继承其实就是将类式继承和构造函数继承组合在一起:


// 声明父类   function Animal(color) {    this.name = 'animal';    this.type = ['pig','cat'];    this.color = color;   }     // 添加共有方法  Animal.prototype.greet = function(sound) {    console.log(sound);   }     // 声明子类   function Dog(color) { // 构造函数继承    Animal.apply(this, arguments);   }   // 类式继承Dog.prototype = new Animal();   var dog = new Dog('白色');   var dog2 = new Dog('黑色');     dog.type.push('dog');   console.log(dog.color); // "白色"console.log(dog.type);  // ["pig", "cat", "dog"]console.log(dog2.type); // ["pig", "cat"]console.log(dog2.color);  // "黑色"dog.greet('汪汪');  // "汪汪"复制代码

在上面的例子中,我们在子类构造函数中执行父类构造函数,在子类原型上实例化父类,这就是组合继承了,可以看到它综合了类式继承和构造函数继承的优点,同时去除了缺陷。

可能你会奇怪为什么组合式继承可以去除类式继承中的引用缺陷?其实这是由于原型链来决定的,由于JavaScript引擎在访问对象的属性时,会先在对象本身中查找,如果没有找到,才会去原型链中查找,如果找到,则返回值,如果整个原型链中都没有找到这个属性,则返回undefined。

也就是说,我们访问到的引用类型(比如上面的type)其实是通过apply复制到子类中的,所以不会发生共享。

这种组合继承也是有点小缺陷的,那就是它调用了两次父类的构造函数。

5、寄生组合式继承

寄生组合式继承强化的部分就是在组合继承的基础上减少一次多余的调用父类的构造函数:

function Animal(color) {this.color = color;this.name = 'animal';this.type = ['pig', 'cat'];}Animal.prototype.greet = function(sound) {console.log(sound);}function Dog(color) {Animal.apply(this, arguments);this.name = 'dog';}/* 注意下面两行 */Dog.prototype = Object.create(Animal.prototype);Dog.prototype.constructor = Dog;Dog.prototype.getName = function() {console.log(this.name);}var dog = new Dog('白色');   var dog2 = new Dog('黑色');     dog.type.push('dog');   console.log(dog.color);   // "白色"console.log(dog.type);   // ["pig", "cat", "dog"]console.log(dog2.type);  // ["pig", "cat"]console.log(dog2.color);  // "黑色"dog.greet('汪汪');  //  "汪汪"复制代码

在上面的例子中,我们并不像构造函数继承一样直接将父类Animal的一个实例赋值给Dog.prototype,而是使用Object.create()进行一次浅拷贝,将父类原型上的方法拷贝后赋给Dog.prototype,这样子类上就能拥有了父类的共有方法,而且少了一次调用父类的构造函数。

Object.create()的浅拷贝的作用类式下面的函数:

function create(obj) {function F() {};F.prototype = obj;return new F();}复制代码

这里还需注意一点,由于对Animal的原型进行了拷贝后赋给Dog.prototype,因此Dog.prototype上的constructor属性也被重写了,所以我们要修复这一个问题:

Dog.prototype.constructor = Dog;复制代码

6、extends继承

Class和extends是在ES6中新增的,Class用来创建一个类,extends用来实现继承:


class Animal {   constructor(color) {   this.color = color;   }   greet(sound) {   console.log(sound);   }  }   class Dog extends Animal {   constructor(color) {   super(color);   this.color = color;   }  }   let dog = new Dog('黑色');  dog.greet('汪汪');  // "汪汪"console.log(dog.color); // "黑色"复制代码

在上面的代码中,创建了父类Animal,然后Dog子类继承父类,两个类中都有一个constructor构造方法,实质就是构造函数Animal和Dog。

不知道你有没有注意到一点,我在子类的构造方法中调用了super方法,它表示父类的构造函数,用来新建父类的this对象。

注意:子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。


  • 子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象。

  • ES5的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6的继承机制完全不同,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this。

  • 在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,是基于对父类实例加工,只有super方法才能返回父类实例。


function Parent5 () {this.name = 'parent5';this.play = [1, 2, 3];
}
function Child5 () {Parent5.call(this);this.type = 'child5';
}
Child5.prototype = Object.create(Parent5.prototype);
Child5.prototype.constructor = Child5;     //重新修改了Child5的构造函数为Child5复制代码

JavaScript实现继承的方式相关推荐

  1. JavaScript实现继承的方式,不正确的是:

    JavaScript实现继承的方式,不正确的是:DA.原型链继承 B.构造函数继承 C.组合继承 D.关联继承 解析 javaScript实现继承共6种方式: 原型链继承.借用构造函数继承.组合继承. ...

  2. JavaScript实现继承的方式和各自的优缺点

    ECMAscript只支持实现继承,主要是依靠原型链来实现的. JavaScript实现继承的方式: 类式继承 构造函数继承 组合继承 寄生组合式继承 1.类式继承 1 //类式继承 2 //声明父类 ...

  3. jQuery-源码阅读,JavaScript原生继承方式与jQuery中的继承

    JavaScript中继承方法有以下几种: 1.原型链继承: function Book (name,author){this.name=name;this.author=author;}Book.p ...

  4. javascript实现继承的七种方式(from 红宝书)

    继承是面向对象语言的基础概念,一般OO语言支持两种继承方式:接口继承和实现继承.接口继承只继承方法签名,而实现继承则继承实际的方法.ECMAScript中函数没有签名,因此无法实现接口继承.ECMAS ...

  5. 作为前端,你需要懂得javascript实现继承的方法

    在ES6之前,javascript不跟其他语言一样,有直接继承的方法,它需要借助于构造函数+原型对象模拟实现继承.现在我们可以利用ES6的extends方法实现继承,如果想了解更多有关ES6实现的继承 ...

  6. 白话解释 Javascript 原型继承(prototype inheritance)

    来源: 个人博客 白话解释 Javascript 原型继承(prototype inheritance) 什么是继承? 学过"面向对象"的同学们是否还记得,老师整天挂在嘴边的面向对 ...

  7. JavaScript:继承

    在JavaScript中,有很多地方都涉及到继承的使用,这样不仅可以合理的利用数据,而且可以结合实际开发情况衍生一些特定的属性,我们先定义一个类: function Person(name) {// ...

  8. JavaScript之继承(原型链)

    JavaScript之继承(原型链) 我们知道继承是oo语言中不可缺少的一部分,对于JavaScript也是如此.一般的继承有两种方式:其一,接口继承,只继承方法的签名:其二,实现继承,继承实际的方法 ...

  9. JavaScript的继承,原型和原型链

    前言 想必,学过 java 和 C++ 的小伙伴们,对于继承这个词应该不陌生,最近我也是一直在巩固JavaScript的知识,今天就来一起学习一下JavaScript里的继承吧. 继承是什么? 首先我 ...

最新文章

  1. Windows10+Anaconda3+Pycharm环境搭建
  2. mysql 5000万条数据库_1亿条数据如何分表100张到Mysql数据库中(PHP)
  3. 淘宝用了mysql,您呢?
  4. Java学习开发入门基础教程系列
  5. C语言学习之分别用if和switch编程,输入0-100分成绩,输出相应的成绩档次。
  6. [网络安全自学篇] 二十八.文件上传入门及防御原理(一)
  7. 超经典解释什么叫网关
  8. PHP工厂模式计算面积与周长
  9. 03:计算书费【一维数组】
  10. kali linux下sqlmap使用教程
  11. 关于学校软件安装错误:“an error ocurred installing TAP device”的个人解决办法
  12. DIGITS使用步骤
  13. 鸟哥linux——Ext2文件系统基本原理
  14. 用计算机唱十只兔子,十只兔子为什么是禁歌 这首童谣恐怖在哪里
  15. 【JAVA笔记】JAVA调用同一个包里的不同类的方法:
  16. 管程(Moniter) 并发编程的基本心法
  17. Data Matrix码
  18. 【Golang】Go语言Windows GUI库XCGUI,DirectUI设计思想,高度自定义界面,支持Direct2D硬件加速
  19. 使用Stratasys创建3D打印医学模型的工作流程
  20. vba返回excel中所有菜单命令栏CommandBar的名称

热门文章

  1. java并发编程-volatile内存实现和原理
  2. vice versa VS the other way around
  3. Macaca-iOS入门那些事2
  4. [置顶]大型网站技术架构(一)大型网站架构演化
  5. SQLServer 2000中,存储过程和用户自定义函数具体的区别??
  6. c# 操作 Excel
  7. centos LAMP菜鸟搭建过程
  8. csv数据源的创建(二)
  9. asp.net中怎样动态调用对象事件的处理方法?
  10. Ubuntu ibus 输入法之Skype不能输入中文