Class可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多;

class Father {}
class Son extends Father {
}

代码定义了一个Son 类,该类通过extends关键字,继承了Father类的所有属性和方法,但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个Father类。

class Son extends Father {constructor (name,age,city) {super(name,age);//调用父类的constructor(name,age);this.city = city;}toString () { return this.city+ " " +super.toString();//调用父类的toString()}
}

constructor方法和toString方法之中,都出现了super关键字,他在这里表示父类的构造函数,用来新建父类的this对象;

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

class Father {   }class Son extends Father {constructor(){  }
}
let s = new Son();
//referenceError : this is not defined 

Son继承了父类Fatherm,但是他的构造函数没有调用super方法,这导致新建实例时报错;
ES5的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上(Parent.apply(this)),ES6的继承机制完全不同,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this;
如果子类没有定义constructor方法,这个方法会默认添加,也就是说,不管有没有显式定义,任何一个子类都有constructor方法。

class Son extends Father {
}//等同于
class Son extends Parent {constructor(...args) {super(...args);}
}

另一个需要注意的是:在子类的构造函数中,只有调用super之后,才能使用this关键字,否则会报错。这是因为子类实例的构建,是基于对父类实例加工,只有super方法才能返回父类实例;

class Father {constructor (x,y) {this.x= x;this.y = y;}
}class Son extends Father {constructor (x, y, color) {this.color =color ;//ReferenceError : this is not definedsuper(x,y);this.color = color;//正确}
}let s = new Son(25,8,"green");
s instanceof Son //true
s instanceof Father //true

子类的constructor方法没有调用super之前,就使用this关键字,结果报错,而放在super方法之后就是正确的;

Object.getPrototypeOf()方法用来从子类上获取父类

Object.getPrototypeOf( Son ) ===Father
//true
//因此可以用这个方法判断,一个类是否继承了另一类

super 关键字
super这个关键字,既可以当作函数使用,也可以当作对象使用,
(1)第一情况是:super当作函数调用时,代表父类的构造函数,ES6要求,子类的构造函数必须执行一个super函数;

class Father { }class Son extends Father {constructor () {super();}
}
//子类Son的构造函数之中的super(),代表调用父类的构造函数。这是必须的,否则 JavaScript 引擎会报错。

super虽然代表了父类Father的构造函数,但是返回的是子类Son的实例,即super内部的this指向的是Son,因此super()在这里相当于Father.constructor.call(this);
而且作为函数时,super()只能用在子类的构造函数中,在其他地方会报错;

class A {constructor (){console.log(new.target.name);}}class B extends A {constructor () {super();}}new A()//Anew B()//B 

new.target指向当前正在执行的函数,在super()执行时,他指向的是子类B的构造函数,而不是父类A的构造函数,super()内部的this指向的是B;

(2)第二种情况,super作为对象时,在普通方法中,指向父类的原型对象,在静态方法中,指向父类;

class Father{getName ( ) {return "MGT360124";}
}
class Son extends Father {constructor () {super();console.log(super.getName() )  //“MGT360124”}
}
let s = new Son();

子类Son中的super.p()就是将super当作一个对象使用,这时,super在普通方法中,指向Father.prototype,所以super.getName()就相当于Father.prototype.getName();//"MGT360124",由于super指向父类的原型对象,所以定义在父类实例上的方法或者属性,是无法通过super调用的;

class Father {constructor () {this.p  =2}
}class Son extends Father {get  m ( ) {return super.p;}getValue ( ) {return super.a;}
}
let  s = new Son();
s.m
//undefined

p是父类Father实例的属性,super.p就引用不到它

如果属性定义在父类的原型对象上,super就可以取到。

class A {}
A.prototype.x = 2;class B extends A {constructor() {super();console.log(super.x) // 2}
}let b = new B();

属性x是定义在A.prototype上面的,所以super.x可以取到它的值。

ES6 规定,通过super调用父类的方法时,super会绑定子类的this。

class  Father {constructor () {this.x =1;//这个this指向的是Father对象的实例}print () {console.log(this.x);}
}class Son extends Father {constructor () {super();this.x = 2;//这个this指向的是Son对象的实例}m() {super.print();       }
}
let s = new  Son();
s.m();
//2 

super.print()虽然调用的是Father.prototype.print(),但是Father.prototype.print()会绑定子类Son的this,导致输出的是2,而不是1,也就是说,实际上执行的是 super.print.call(this)。

如果super作为对象,用在静态方法中,这时super将指向父类,而不是父类的原型对象;

class Parent {static myMethod (msg) {console.log("static",msg);}myMethod (msg) {console.log("instance" ,msg);}
}class Child  extends Parent {static myMethod(msg) {super.myMethod(msg);}myMethod (msg) {super.myMethod(msg);}}Child.myMethod(1);
//static 1
var child = new Child();
child.myMethod(2);
//instance 2

super在静态方法之中指向父类,在普通方法之中指向父类的原型对象。
注意,使用super的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。
类的prototype属性和proto属性
大多数浏览器的ES5实现之中,每一个对象都有proto属性,指向对应的构造函数的prototype属性,class作为构造函数的语法糖,同时有prototype属性和proto属性,因此同时存在两条继承链;
(1)子类的proto属性,表示构造函数的继承,总是指向父类;
(2)子类prototype属性的proto属性,表示方法的继承,总是指向父类的prototype属性;

class A{
}
class B{
}
//B的实例继承A的实例
Object.setPrototypeOf(B.prototype, A.prototype);//B 的实例继承A的静态属性
Object.setPrototypeOf(B,A);const b = new B();

《对象的扩展》一章中Object.setPrototypeOf()方法的实现:

Object.setPrototypeOf = function (obj, proto) {
obj.__proto__ = proto;return obj ;
}

因此

Object.setPrototypeOf( B.prototype ,  A.prototype );
//等同于
B.prototype.__proto__ =  A.prototype ;Object.setPrototypeOf(B, A);
//等同于
B.__proto__ = A;

这两条继承链,可以理解为:作为一个对象,子类B的原型(proto属性)是父类(A);作为一个构造函数,子类B的原型对象(prototype属性)是父类的原型对象(prototype)的实例;

extends的继承目标
extends关键字后面可以跟很多类型的值;

class B extends A{
}

只要A有一个prototype属性的函数,就能被B继承,由于函数都有prototype属性(除了Function.prototype函数),因此A可以使任意函数,下面三种情况:
(1)子类继承Object类

class A extends Object {
}
A.__proto__ === Object //true;
A.prototype.__proto__ === Object.prototype //true

这种情况就是 : A就是构造函数Object的复制,A的实例就是Object的实例
(2)不存在任何继承

class A {
}
A.__proto__ === Function.prototype //true
A.prototype.__proto__ = Object.prototype //true

这种情况是:A作为一个基类(不存在任何继承),就是一个普通的函数,所以直接继承Function.prototype。但是A调用后返回一个空对象(即Object实例),所以A.prototype.proto指向构造函数(Object)的prototype属性;
实例的proto属性
子类实例的proto属性的proto属性,指向父类实例的proto属性。也就是说,子类的原型的原型,是父类的原型。

原生构造函数的继承
原生构造函数是指语言内置的构造函数,通常用来生成数据结构。

Boolean()
Number()
String()
Array()
Date()
Function()
RegExp()
Error()
Object()

extends关键字不仅可以用来继承类,还可以用来继承原生的构造函数。因此可以在原生数据结构的基础上,定义自己的数据结构。

阮一峰的ES6---Class的继承相关推荐

  1. 实现阮一峰ES6的顶部加载条效果

    效果例子 阮一峰的ES6:http://es6.ruanyifeng.com/?search=s&x=13&y=3 html + css <style type="te ...

  2. js -- ES6(一)-- 简介(根据阮一峰ES6标准入门整理)

    目前正在学习ES6,根据阮一峰的ES6入门2,学到哪更新到哪里,都是基本的知识,复杂的目前还不会,涉及的代码都是亲自运行过的,若发现错误请指正. ES6 提供了许多新特性,但是并不是所有的浏览器都能够 ...

  3. 阮一峰es6电子书_ES6理解进阶【大前端高薪训练营】

    一:面向对象:类class 面向对象三大特性之封装 封装是面向对象的重要原则,它在代码中的体现主要是以下两点: 封装整体:把对象的属性和行为封装为一个整体,其中内部成员可以分为静态成员(也叫类成员)和 ...

  4. 阮一峰 / ES6 数组的解构赋值

    目录 一.定义 二.详情讲解 1.数组解构:数组解构时数组的元素是按次序排列的,变量的取值由它的位置决定 2.对象解构:对象解构时对象的属性没有次序,变量必须与属性同名,才能取到正确的值. 三.用途 ...

  5. 【ES6】阮一峰promise

    阅读链接:[ES6]阮一峰promise 要点: then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例).因此可以采用链式写法,即then方法后面再调用另一个then方 ...

  6. 阮一峰ES6入门读书笔记(十六):Moudle

    阮一峰ES6入门读书笔记(十六):Moudle 在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种.前者用于服务器,后者用于浏览器.ES6 在语言标准的层面上 ...

  7. 阮一峰ES6入门读书笔记(七):运算符的拓展

    阮一峰ES6入门读书笔记(七):运算符的拓展 1. 指数运算符 ES6新增了一个指数运算符(**). 2 ** 2 // 4 2 ** 3 // 8 这个运算符的一个特点是右结合,而不是常见的左结合. ...

  8. ES6 标准入门(第二版)阮一峰学习

    现在前端环境中,每一位程序员都要求熟练ES6语法,但是大部分ES6的文档都不太完整,接下来的时间,我将为童鞋们分享阮一峰老师第二版的ES6标准.让我们一起来学习一下!!! 本期先说一下学习的目录 1: ...

  9. ES5和ES6中对于继承的实现方法

    在ES5继承的实现非常有趣的,由于没有传统面向对象类的概念,Javascript利用原型链的特性来实现继承,这其中有很多的属性指向和需要注意的地方. 原型链的特点和实现已经在之前的一篇整理说过了,就是 ...

  10. ES5、ES6 如何实现继承

    完整高频题库仓库地址:https://github.com/hzfe/awesome-interview 完整高频题库阅读地址:https://febook.hzfe.org/ 相关问题 关于 ES5 ...

最新文章

  1. opencv +数字识别
  2. 基于代价函数小波脊相位的MFSK信号符号速率估计MATLAB仿真及代码(2020.12.14更新)
  3. 2019计算机国二操作题,2019年3月计算机二级C++操作练习题及答案(十二)
  4. Python学习笔记-基本语法
  5. c#提交事务的两种方法
  6. 安装mysql connector odbc后在控制面板 数据源下没有找到mysql的驱动
  7. python全套完整教程-Python全套教程
  8. (原创)用cmd命令制作恶搞程序
  9. python导入鸢尾花数据集_python KNN算法实现鸢尾花数据集分类
  10. python上传文件到云服务器,python基于paramiko将文件上传到服务器代码实现
  11. Vivado synth 8-439 module““not found问题绝对解决,超简单,想解决问题就看这里。
  12. git pull 失败:Failed to connect to 127.0.0.1 port 10080: Connection refused
  13. 微信获取用户地理位置
  14. Opencv 提取水平 垂直线,去除杂线,提取对象
  15. PMP-PMBOK-培训(7)Initiating a Project and Preparing the Project Plan
  16. python如何截取视频中的某一段
  17. idea如何查看已安装的插件
  18. go语言编写php扩展,[原创]快速开发一个PHP扩展-Go语言中文社区
  19. 为什么我愿意来北上广打拼?
  20. python中oo是什么意思_python中的OO

热门文章

  1. USB原理之一连接建立(四)2022-02-13
  2. 山东大学软件学院创新实训——飞讯(八)
  3. Subscription to topics, partitions and pattern are mutually exclusive java
  4. QT6 Mingw-w64 opencv环境配置教程
  5. java文件下载框架,使用Struts 2框架实现文件下载 - 消逝の纸屑
  6. Linux 部署表白墙程序
  7. 英飞凌单片机知乎_AURIX学习篇——《英飞凌多核单片机应用技术(AURIX三天入门篇)》-第5章 GTM驱动波形合成...
  8. 在线SVG转png的网站
  9. 声音引擎Wwise与Criware的区别
  10. 软件开发流程--学习笔记