美文网首页
ES6-class的继承

ES6-class的继承

作者: 吴高亮 | 来源:发表于2019-01-28 22:08 被阅读0次

    Classk可以通过extends关键字实现继承;这比ES5的通过修改原型链实现继承,要清晰和方便很多;

    class Point{
    
    }
    class ColorPoint extends Point{
    
    }
    

    上面的代码定义了一个ColorPoint类;该类通过extends关键字,继承了Piont类的所有方法和属性;但是由于没有部署任何代码,所以这连个类完全一样;等于复制了一个Point类;。下面再ColorPoint内部加上代码;

    class ColorPoint extends Piont{
     constructor(x,y,color){
        super(x,y);//调用父类的constructor(x,y);
        this.color=color;
      }
      toString(){
         return this.color+' '+super.toString();调用父类的toString
      }
    };
    

    上面代码中,constructor方法和toString方法之中,都出现了super关键字,它在这里表示父类的构造函数,用来新建父类的this对象。

    子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象

    class Point { /* ... */ }
    
    class ColorPoint extends Point {
      constructor() {class ColorPoint extends Point {
    }
    
    // 等同于
    class ColorPoint extends Point {
      constructor(...args) {
        super(...args);
      }
    }
      }
    }
    
    let cp = new ColorPoint(); // ReferenceError
    

    上面代码中,ColorPoint继承了父类Point,但是它的构造函数没有调用super方法,导致新建实例时报错。

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

    如果子类没有定义constructor方法,这个方法会被默认添加,代码如下。也就是说,不管有没有显式定义,任何一个子类都有constructor方法。

    class ColorPoint extends Point {
    }
    
    // 等同于
    class ColorPoint extends Point {
      constructor(...args) {
        super(...args);
      }
    }
    

    另一个需要注意的地方是,在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有super方法才能调用父类实例。

    class Point {
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }
    }
    
    class ColorPoint extends Point {
      constructor(x, y, color) {
        this.color = color; // ReferenceError
        super(x, y);
        this.color = color; // 正确
      }
    }
    

    上面方法;在没有使用super的时候;使用this关键字报错;在使用了super之后就正确了
    下面是生成子类实例的方法;

    let cp=new ColorPoint(25,8,'green');
    cp instanceof ColorPoint();//true;
    cp instanceof point();//true;
    

    上面代码中;cp同时是两个是实例这个ES5的规范相同;
    最后父类的静态方法也会被子类继承;

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

    hello是A的静态方法;B继承了A 也继承了A的静态方法;

    Object.getPrototypeof();可以通过子类获取父类;

    Object.getPrototypeof()===Point //true;

    super;

    super既可以当做对象使用;同样也可以当做函数使用;这两种情况下他的结果都是不一样'
    第一种情况;当做函数使用的时候;作为父类的构造函数;ES6要求子类的构造函数必须执行一次super函数;

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

    上面的代码;在B的构造函数执行super代表着调用父类的构造函数;这是必须的;否者js会报错;
    注意;这里的super的代表着A父类的构造函数;但是却返回子类B的实例;即super的内部的this是执行B的;因此super()在这里相当于A.prototype.constructor.call(this);

    class A{
     constructor(){
       console.log(new.target.name);
     }
    };
    class B extends A{
     constructor(){
        super();
      }
    }
    new A();//A;
    new B();//B;
    

    上面的的代码,new.target指向正在执行的函数;.可以看出来在super()执行时;他指向的是子类B的构造函数;而不是父类A的构造函数。也就是说,super()内部的this指向的是B。

    作为函数的时候;只能在子类的构造函数中执行;不可以用在其他地方执行;

    class A {}
    
    class B extends A {
      m() {
        super(); // 报错
      }
    }
    

    上面代码中,super()用在B类的m方法之中,就会造成句法错误。
    第二种方式 super 代表对象;在子类的方法中;指向父类的原型对象;在静态方法中;指向父类;

    class A {
      constructor() {
        this.p = 2;
      }
    }
    
    class B extends A {
      get m() {
        return super.p;
      }
    }
    
    let b = new B();
    b.m // undefined
    

    上面代码中,p是父类A实例的属性,super.p就引用不到它。

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

    class A{};
    A.prototype.x=2;
    class B extends A{
    consturctor(){
     super()
     console.log(super.x)
    }
    }
    

    上面的代码x是定义在A的原型上;
    ES6 规定,在子类普通方法中通过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();
      }
    }
    
    let b = new B();
    b.m() // 2
    

    上面代码中,super.print()虽然调用的是A.prototype.print(),但是A.prototype.print()内部的this指向子类B的实例,导致输出的是2,而不是1。也就是说,实际上执行的是super.print.call(this)。

    由于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
      }
    }
    
    let b = new B();
    

    上面的代码中指的是;super.x赋值为3;这时候赋值等同于this.x=3;但是去取值的时候时间上是取super的值是指的父类的值;相当于读的是A.prototype.x所以是undefined;

    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调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例。

    class A {
      constructor() {
        this.x = 1;
      }
      static print() {
        console.log(this.x);
      }
    }
    
    class B extends A {
      constructor() {
        super();
        this.x = 2;
      }
      static m() {
        super.print();
      }
    }
    
    B.x = 3;
    B.m() // 3
    
    

    相关文章

      网友评论

          本文标题:ES6-class的继承

          本文链接:https://www.haomeiwen.com/subject/qtbzjqtx.html