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. 2015年国际智慧教育展览会盛大开幕
  3. fir fpga 不同截止频率_【通信篇】带你认识FIR滤波器
  4. java前后端分离账号错误_前后端分离,获取token,验证登陆是否失效
  5. python3.8安装pygame_Python3.8安装Pygame教程步骤详解
  6. 基于HBASE的并行计算架构之rowkey设计篇
  7. 第十二单元文件的归档/压缩/传输
  8. 数据表的查询 sqlserver 基本的三个查询
  9. Netty工作笔记0069---Protobuf使用案例
  10. c语言求100∑k=1k 30,C语言程序设计〔第三章〕.ppt
  11. java获取发件人_如何获取发件人outlook / Exchange的SMTP地址
  12. Rust: tokio,异步代码与运行速度初探
  13. 5种常用的相关分析方法
  14. Cortex-M可以跑Linux操作系统吗?
  15. Wannafly Winter Camp 2019 Day2 H Cosmic Cleaner (球体相交体积(球冠体积公式))
  16. 显卡对应的Compute Capability值
  17. 1419:SPFA(II)
  18. PMSM学习笔记1——永磁同步电机的工作原理与数学模型
  19. 《Investigating Typed Syntactic Dependencies for TSC Using GAT》论文笔记
  20. 计算机如何连接wifi台式,台式电脑怎么连wifi

热门文章

  1. leetcode-1697-检查边长度限制的路径是否存在
  2. Kruskal算法详解
  3. XShell 和 WinScp 教程
  4. leetcode-每日一题2021.8.26 救生艇
  5. 2021年安全员-A证(山东省)考试及安全员-A证(山东省)考试技巧
  6. uni-app上传视频
  7. 数据库基础理论三——多值依赖Armstrong公理及特性推导
  8. 【cortex-m3/m4/m7常见死机、跑飞、异常、hardfault等查找方法】
  9. 如何应对 Redis 集群的数据倾斜问题
  10. 命令行 安装 Gparted 工具安装. linux 磁盘分配工具