ES6知识点整理之----Class----继承

Class 可以通过 extends 关键字实现继承。

class Point {
}

class ColorPoint extends Point {
}

子类必须在constructor方法中调用super方法,否则新建实例时会报错。

class Point { /* ... */ }

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关键字,否则会报错。

父类的静态方法,也会被子类继承。

class A {
  static hello() {
    console.log('hello world');
  }
}

class B extends A {
}

B.hello()  // hello world

Object.getPrototypeOf()

用来从子类上获取父类。

Object.getPrototypeOf(ColorPoint) === Point
// true

super 关键字

super这个关键字,既可以当作函数使用,也可以当作对象使用。

  • 当作函数使用时:代表父类的构造函数。
class A {}

class B extends A {
  constructor() {
    super();
  }
}

super()只能用在子类的构造函数之中,用在其他地方就会报错。

class A {}

class B extends A {
  m() {
    super(); // 报错
  }
}
  • 当作对象使用时:在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
class A {
  p() {
    return 2;
  }
}

class B extends A {
  constructor() {
    super();
    console.log(super.p()); // 2
  }
}

let b = new B();

由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。

class A {
  constructor() {
    this.p = 2;
  }
}

class B extends A {
  get m() {
    return super.p;
  }
}

let b = new B();
b.m // undefined

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

class A {}
A.prototype.x = 2;

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

let b = new B();

在子类普通方法中通过super调用父类的方法时,方法内部的this指向当前的子类实例。

扫描二维码关注公众号,回复: 3270784 查看本文章
class A {
  constructor() {
    this.x = 1;
  }
  print() {
    console.log(this.x);
  }
}

class B extends A {
  constructor() {
    super();
    this.x = 2;
  }
  m() {
    super.print();
  }
}

let b = new B();
b.m() // 2

由于this指向子类实例,所以如果通过super对某个属性赋值,这时super就是this,赋值的属性会变成子类实例的属性。

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

在子类的静态方法中通过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例。

注意,使用super的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。

由于对象总是继承其他对象的,所以可以在任意一个对象中,使用super关键字。

类的 prototype 属性和__proto__属性

__proto__属性,指向对应的构造函数的prototype属性。

Class 作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链。

  • 子类的__proto__属性,表示构造函数的继承,总是指向父类。
  • 子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。
class A {
}

class B extends A {
}

B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true

类的继承是按照下面的模式实现的:

class A {
}

class B {
}

// B 的实例继承 A 的实例
Object.setPrototypeOf(B.prototype, A.prototype);

// B 继承 A 的静态属性
Object.setPrototypeOf(B, A);

const b = new B();
  • 作为一个对象,子类(B)的原型(__proto__属性)是父类(A);
  • 作为一个构造函数,子类(B)的原型对象(prototype属性)是父类的原型对象(prototype属性)的实例。

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

  • 子类继承Object类:
class A extends Object {
}

A.__proto__ === Object // true
A.prototype.__proto__ === Object.prototype // true
  • 不存在任何继承
class A {
}

A.__proto__ === Function.prototype // true
A.prototype.__proto__ === Object.prototype // true

实例的 __proto__ 属性

子类实例的__proto__属性的__proto__属性,指向父类实例的__proto__属性。

//ColorPoint 继承 Point
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('Ha');
};

p1.printName() // "Ha"

原生构造函数的继承

ECMAScript 的原生构造函数大致有下面这些:

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

ES5中,生构造函数是无法继承的。

ES6中,允许继承原生构造函数定义子类。

class MyArray extends Array {
  constructor(...args) {
    super(...args);
  }
}

var arr = new MyArray();
arr[0] = 12;
arr.length // 1

arr.length = 0;
arr[0] // undefined

extends关键字不仅可以用来继承类,还可以用来继承原生的构造函数。

注意,继承Object的子类,有一个行为差异。无法通过super方法向父类Object传参

Mixin 模式的实现

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

const a = {
  a: 'a'
};
const b = {
  b: 'b'
};
const c = {...a, ...b}; // {a: 'a', b: 'b'}
function mix(...mixins) {
  class Mix {}

  for (let mixin of mixins) {
    copyProperties(Mix.prototype, mixin); // 拷贝实例属性
    copyProperties(Mix.prototype, Reflect.getPrototypeOf(mixin)); // 拷贝原型属性
  }

  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) { // ... }

猜你喜欢

转载自www.cnblogs.com/adhehe/p/9686840.html