美文网首页程序员iOS开发
ECMAScript 6 - Class的继承

ECMAScript 6 - Class的继承

作者: 庸者的救赎 | 来源:发表于2016-10-19 16:55 被阅读106次

    基本用法

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

    class Test extends Component {}
    

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

    class Test extends Component {
      constructor(props) {
        super(props); // 调用父类constructor(props)
      }
      
      toTest() {
        super.toTest(); // 调用父类toTest()
      }
    }
    

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

    子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super方法,子类就得不到this对象

    class Component {}
    
    class Test extends Component {
      cosntructor() {
        
      }
    }
    
    let a = new Test();
    

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

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

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

    constructor(...args) {
      super(...args);
    }
    

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

    class Component {
      constructor(props) {
        this.props = props;
      }
    }
    
    class Test extends Component {
      cosntructor(props, a) {
        this.a = a; // ReferenceError
        super(props);
        this.a = a; // 正确
      }
    }
    

    上面代码中,子类的constructor方法没有调用super之前,就使用this关键字,结果报错,而放在super方法之后就是正确的。

    类的prototype属性和proto属性

    大多数浏览器的ES5实现之中,每一个对象都有__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
    

    上面代码中,子类B__proto__属性指向父类A,子类Bprototype属性的__proto__属性指向父类Aprototype属性。

    Extends的继承目标

    extends关键字后面可以跟多种类型的值

    class B extends A {}
    

    上面代码的A,只要是一个有prototype属性的函数,就能被B继承。由于函数都有prototype属性(除了Function.prototype函数),因此A可以是任意函数。

    下面,讨论三种特殊情况。

    第一种特殊情况,子类继承Object类。

    class A extends Object {
      
    }
    
    A.__proto__ === Object;  // true
    A.prototype.__proto__ === Object.prototype;  // true
    

    这种情况下,A其实就是构造函数Object的复制,A的实例就是Object的实例。

    第二种特殊情况,不存在任何继承。

    class A {}
    
    A.__proto__ === Function.prototype;  // true
    A.prototype.__proto__ === Object.prototype;  // true
    

    这种情况下,A作为一个基类(即不存在任何继承),就是一个普通函数,所以直接继承Funciton.prototype。但是,A调用后返回一个空对象(即Object实例),所以A.prototype.__proto__指向构造函数(Object)的prototype属性。

    第三种特殊情况,子类继承null

    class A extends null {
      
    }
    
    A.__proto__ === Function.prototype;  // true
    A.prototype.__proto__ === undefined;  // true
    

    这种情况与第二种情况非常像。A也是一个普通函数,所以直接继承Funciton.prototype。但是,A调用后返回的对象不继承任何方法,所以它的__proto__指向Function.prototype,即实质上执行了下面的代码。

    class C extends null {
      constructor() {
        return Object.create(null);
      }
    }
    

    Object.getPrototypeOf()

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

    Object.getPrototypeOf(A) === B;
    

    因此,可以使用这个方法判断,一个类是否继承了另一个类。

    super关键字

    super这个关键字,有两种用法,含义不同。

    • 作为函数调用时(即super(...args)),super代表父类的构造函数
    • 作为对象调用时(即super.propsuper.method()),super代表父类。注意,此时super即可以引用父类实例的属性和方法,也可以引用父类的静态方法
    class B extends A {
      get m() {
        return this._p * super._p;
      }
      
      set m() {
        throw new Error('该属性只读');
      }
    }
    

    上面代码中,子类通过super关键字,调用父类实例的_p属性。

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

    var obj = {
      toString() {
        return "MyObject: " + super.toString();
      }
    };
    
    obj.toString(); // MyObject: [object Object]
    

    生命不息,折腾不止...
    I'm not a real coder, but i love it so much!

    相关文章

      网友评论

        本文标题:ECMAScript 6 - Class的继承

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