Class的继承

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


简洁

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 // true
cp 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() // A
new 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 = 2

class B extends A{
  constructor(){
    super()
    console.log(super.x)
  }
}

let b = new B() // 2

// ES6规定,通过super调用父类的方法时,super会绑定子类的this
class 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 1

var 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 // true
B.prototype.__proto__ === A.prototype // true
extends的继承目标

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

以下讨论三种特殊情况:

// 第一种:子类继承Object类。
class A extends Object{}
// 这时A就是构造函数Object的复制,A的实例就是Object实例
A.__proto__ === Object // true
A.prototype.__proto__ === Object.prototype // true

// 第二种:不存在任何继承。
class A{}
// A作为一个基类(即不存在任何继承),就是一个普通函数,直接继承Function.prototype。A调用后返回一个空对象(即Object实例)
A.__proto__ === Function.prototype // true
A.prototype.__proto__ === Object.prototype // true

// 第三种:继承null
class A extends null{}
// 和第二种情况类似,A是一个普通函数,直接继承Function.prototype
A.__proto__ === Function.prototype // true
A.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__ // false
p2.__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] = 12
arr.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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值