FrankKai / FrankKai.github.io

FE blog
https://frankkai.github.io/
362 stars 39 forks source link

es6 class 之 super #202

Open FrankKai opened 4 years ago

FrankKai commented 4 years ago

class在面向对象设计中非常有用,super关键字值得好好学习一下。

super关键字概览

初识

super关键字用于调用对象的父对象的函数super.propsuper[expr]表达式对于classed和object literals方法定义都是有效的。

语法

super([arguments]); // 调用parent的constructor
super.functionOnParent([arguments]);

描述

在使用constructor时,super关键字单独出现而且必须要在this关键词之前使用。 super同样可以调用父对象的函数。

super关键字常用示例

在constructor中使用super

下面的例子使用super()关键词为Square继承了Rectangle构造器中的属性。

class Rectangle {
  constructor(height, width) {
    this.name = 'Rectangle';
    this.height = height;
    this.width = width;
  }
  ...
}
class Square extends Rectangle {
  constructor(length) {
    this.height; // ReferenceError, super needs to be called first!
    super(length, length);
    this.name = 'Square';
  }
}

使用super调用static方法

可以使用super.xxx()调用父class的static方法。

class Rectangle {
  constructor() {}
  static logNbSides() {
    return 'I have 4 sides';
  }
}
class Square extends Rectangle {
  constructor() {}
  static logDescription() {
      return super.logNbSides() + ' which are all equal';
  }
}
Square.logDescription(); // 'I have 4 sides which are all equal'

删除super属性会抛出错误

不能用delete操作符删除super.prop或super[expr],会抛出一个ReferenceError。

class Base {
  constructor() {}
  foo() {}
}
class Derived extends Base {
  constructor() {}
  delete() {
    delete super.foo; // this is bad
  }
}
new Derived().delete(); // ReferenceError: invalid delete involving 'super'. 

super.prop无法重写非writable的属性

当使用Object.defineProperty定义一个非可写属性时,super不能重写这个值。

class X {
  constructor() {
    // 这里定义了一个read-only的prop属性
    Object.defineProperty(this, 'prop', {
      configurable: true,
      writable: false, 
      value: 1
    });
  }
}

class Y extends X {
  constructor() {
    super();
  }
  foo() {
    super.prop = 2;   // Cannot overwrite the value.
  }
}

var y = new Y();
y.foo(); // TypeError: "prop" is read-only
console.log(y.prop); // 1

在object 迭代过程中使用super.prop

super不仅仅在class中使用,在object中也可以使用。 需要使用Object.setPrototypeOf。

var obj1 = {
    method1() {
        console.log('method1');
    }
}
var obj2 = {
    method2() {
        super.method1(); // 惊不惊喜,对象也能用super
    }
}
Object.setPrototypeOf(obj2, obj1);
obj2.method2(); // 打印出"method1"

super实践疑惑

印象中java可以直接super,es6的class可以直接super()吗?

看看下面这个例子。

super(); // 仅继承父的prototype method
super(prop1, prop2); // 继承父的constructor和prototype method

可以是可以。 但是不会继承父constructor,只会继承父的prototype方法。 所以最好还是写上要继承哪个prop。

需要做以下几步: 1.子class的constructor传入自己的入参,constructor(prop1,prop2) 2.在super中指明要继承父class的属性,super(prop1,prop2,...)

class Base {
  constructor(name, age) {
      this.name = name;
      this.age = age;
  }
  foo(){
      console.log("Hi, I'm foo");
  }
}
class Sub extends Base {
    constructor(name, age){
        // super(); wrong 若是这样,未继承父的this.name = name
        super(name); // right 这样才对,继承了父的this.name = name
        console.log(this.name);  // 打印出名字。
        super.foo(); // 无论是super(),还是super(name)。都可以打印出foo
    }
}
var foo = new Sub('gaokai', 25);

可以不在constructor中调用super(),直接使用super.xxx()吗?

不可以。 若你这样做了,会报下面这样的错。

class Sub extends Base {
    constructor(name, age){
        super.foo();
    }
}

ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor

到底要将super()理解成什么?

有人说super()可以理解成ParentClass.constructor(),是这样吗?

只说对了一半。

super()可以理解为下面两部分的集合:

  1. 继承ParentClass.constructor()
  2. super包含父parent class的prototype方法集

static方法可以通过super获取到吗?不能。 public定义的变量可以通过super获取到吗?不能。

所以我们一般都是使用super(foo,bar)这样的形式。 调用之后它做到了:

  1. 继承了父class的constructor到当前class,ParentClass.constructor(foo, bar)
  2. super中包含父class中的prototype方法集。

参考资料:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super