博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Class的继承
阅读量:6511 次
发布时间:2019-06-24

本文共 5873 字,大约阅读时间需要 19 分钟。

本系列属于阮一峰老师所著的学习笔记


简洁

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

// 用法class Point{}class ColorPoint extends Points{}// super关键字,用来表示父类的构造函数,用来新建父类的this对象class ColorPoint extends Point {  constructor(x,y,color){    super(x,y) // 调用父类的constructor(x,y)    this.color = color  }  toString(){    return this.color + ' ' + super.toString() // 调用父类的toString()  }}// 子类必须在constructor方法中调用super方法,否则新建实例会报错。这是因为子类没有自己的this对象,而是继承父类的对象,然后对其加工。如果不调用super方法,子类就得不到this对象class ColorPoint extends Point{   constructor(){}}let cp = new ColorPoint() // ReferenceError

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

// 若子类没有定义constructor方法,该方法会被默认添加class ColorPoint extends Point{}//等同于class ColorPoint extends Point{  constructor(...args){    super(...args)  }}// 在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建是基于对父类实例的加工,只有super方法才能返回父类实例class ColorPoint extends Point{  constructor(x,y,color){    this.color = color // ReferenceError    super(x,y)    this.color = color // 正确  }}// 子类创建的实例,同时是父类的实例let cp = new ColorPoint(23,2,'green')cp instanceof ColorPoint // truecp instanceof Point // true// 父类的静态方法,也会被子类继承class A{  static hello(){    console.log('hello world')  }}class B extends A{}B.hello() // hello world
Object.getPrototypeOf()

Object.getPrototypeOf方法可以用来从子类上获取父类

Object.getPrototypeOf(ColorPoint) === Point // true
super关键字

super关键字,即可以当做函数使用,也可以当做对象使用

// super作为函数调用,代表父类的构造函数。ES6要求,子类的构造函数必须执行一次super函数。作为函数时,super()只能用在子类的构造函数中,用在其他地方会报错class A{}class B extends A{  constructor(){    super()  }}// 虽然super代表了父类A的构造函数,但是返回的是子类B的实例class A{  constructor(){    console.log(new.taget.name)  }}class B extends A{  constructor(){    super()  }}new A() // Anew B() // B// super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类class A{  p(){    return 2  }}class B extends A{  constructor(){    super()    console.log(super.p())  }}// super.p()相当于A.prototype.p()let b = new B() // 2// 由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。如果定义在父类的原型对象上,super就可以取到class A{}A.prototype.x = 2class B extends A{  constructor(){    super()    console.log(super.x)  }}let b = new B() // 2// ES6规定,通过super调用父类的方法时,super会绑定子类的thisclass A{  constructor(){    this.x = 1  }  print(){    console.log(this.x)  }}class B extends A{  constructor(){    super()    this.x = 2  }  m(){    super.print()  }}// super.print()虽然调用的是A.prototype.print(),但是A.prototype.print()会绑定子类B的this,导致输出的是2,而不是1。实际上执行的是super.print.call(this)let b = new B()b.m() // 2// 由于super绑定子类的this,如果通过super对某属性赋值,这时super就是this,赋值的属性会变成子类实例的属性class A{  constructor(){    this.x = 1  }}class B extends A{  constructor(){    super()    this.x = 2    super.x = 3    console.log(super.x) // undefined    console.log(this.x) // 3  }}// super.x赋值3等同于this.x赋值3,而读取super.x时读取的是A.prototype.x,返回undefined// 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)  }}// super在静态方法中指向父类,在普通方法中指向父类的原型对象Child.myMethod(1) // static 1var child = new Child()child.myMethod(2) // instance 2
类的prototype属性和__ proto __属性

大多数浏览器在ES5的实现中,都有一个__proto__属性,指向对应构造函数的prototype属性。Class作为构造函数的语法糖,同时有prototype属性和__proto__属性,同时存在两条继承链。

(1)子类的__proto__属性,表示构造函数的继承,总是指向父类

(2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性

class A{}class B extends A{}B.__proto__ === A // trueB.prototype.__proto__ === A.prototype // true
extends的继承目标

extends关键字后面可以跟多种类型的值,只要是一个具有prototype属性的函数,就能被继承。

以下讨论三种特殊情况:

// 第一种:子类继承Object类。class A extends Object{}// 这时A就是构造函数Object的复制,A的实例就是Object实例A.__proto__ === Object // trueA.prototype.__proto__ === Object.prototype // true// 第二种:不存在任何继承。class A{}// A作为一个基类(即不存在任何继承),就是一个普通函数,直接继承Function.prototype。A调用后返回一个空对象(即Object实例)A.__proto__ === Function.prototype // trueA.prototype.__proto__ === Object.prototype // true// 第三种:继承nullclass A extends null{}// 和第二种情况类似,A是一个普通函数,直接继承Function.prototypeA.__proto__ === Function.prototype // trueA.prototype.__proto__ === undefined // true// A调用后返回的对象不继承任何方法,__proto__指向Function.prototype,执行以下代码class C extends null{  constructor(){ return Object.create(null) }}
实例的__ proto __属性
var p1 = new Point(2,3)var p2 = new ColorPoint(2,3,'red')p2.__proto__ === p1.__proto__ // falsep2.__proto__.__proto__ === p1.__proto__ // true// 以上我们可以通过子类实例的__proto__.__proto__属性,可以修改父类实例的行为p2.__proto__.__proto__.printName = function(){  console.log('Angus')}p1.printName() // 'Angus'
原生构造函数的继承

ES5原生构造函数(Boolean()Number()String()Array()Date()Function()RegExp()Error()Object())是不允许继承的,ES6运行原生构造函数定义子类。因为ES6是先新建父类的实例对象this,然后再用子类的构造函数修饰this,使得父类所有行为都可以继承。

class MyArray extends Array{  constructor(...args){    super(...args)  }}var arr = new MyArray()arr[0] = 12arr.length // 1// 注意,继承Objcet的子类,有一个行为差异class NewObj extends Object{  constructor(){    super(...arguments)  }}var o = new NewObj({attr:true})o.attr === true // false// NewObj继承了Object,但是无法通过super方法向父类Object传参。这是因为ES6改变了Object构造函数的行为,一旦发现Object方法不是通过new Object()这种形式调用,ES6规定Object构造函数会忽略参数
Mixin模式的实现

Mixin指的是多个对象合成一个新的对象,新对象具有各个组成员的接口

// 最简单的实现方法const a = {  a: 'a'}const b = {  b: 'b'}const c = {...a,...b}// 比较完备的实现,将多个类的接口mixin另一个类function mix(...mixins) {  class Mix {}  for (let mixin of mixins) {    copyProperties(Mix, mixin) // 拷贝实例属性    copyProperties(Mix.prototype, mixin.prototype) // 拷贝原型属性  }  return Mix}function copyProperties(target, source) {  for (let key of Reflect.ownKeys(source)) {    if ( key !== "constructor"      && key !== "prototype"      && key !== "name"    ) {      let desc = Object.getOwnPropertyDescriptor(source, key)      Object.defineProperty(target, key, desc)    }  }}// 使用时,继承这个类即可class DistributedEdit extends mix(Loggable, Serializable) {  // ...}

转载于:https://www.cnblogs.com/pengzhixin/p/7744793.html

你可能感兴趣的文章
FreeBinary 格式说明
查看>>
使用Spring Cloud和Docker构建微服务
查看>>
NB-IoT的成功商用不是一蹴而就
查看>>
九州云实战人员为您揭秘成功部署OpenStack几大要点
查看>>
1.电子商务支付方式有哪些 2.比较不同支付方式的优势劣势
查看>>
医疗卫生系统被爆漏洞,7亿公民信息泄露……
查看>>
神秘函件引发的4G+与全网通的较量
查看>>
CloudCC:智能CRM究竟能否成为下一个行业风口?
查看>>
追求绿色数据中心
查看>>
Web开发初学指南
查看>>
探寻光存储没落的真正原因
查看>>
高通64位ARMv8系列服务器芯片商标命名:Centriq
查看>>
中国人工智能学会通讯——融合经济学原理的个性化推荐 1.1 互联网经济系统的基本问题...
查看>>
戴尔为保护数据安全 推出新款服务器PowerEdge T30
查看>>
今年以来硅晶圆涨幅约达40%
查看>>
构建智能的新一代网络——专访Mellanox市场部副总裁 Gilad Shainer
查看>>
《数字视频和高清:算法和接口》一导读
查看>>
《中国人工智能学会通讯》——6.6 实体消歧技术研究
查看>>
如何在Windows查看端口占用情况及查杀进程
查看>>
云存储应用Upthere获7700万美元股权债务融资
查看>>