zoukankan      html  css  js  c++  java
  • 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指向当前的子类实例。

    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) { // ... }
  • 相关阅读:
    Java批量文件打包下载
    Java Swing
    空白文章
    linux 用户创建、管理、权限分配
    在虚拟机下安装hadoop集成环境(centos7+hadoop-2.6.4+jdk-7u79)
    《转载》POI导出excel日期格式
    java导出生成word(类似简历导出)
    《sqoop实现hdfs中的数据导出至mysql数据库》
    c# winform 自动关闭messagebox 模拟回车
    Ubuntu下启动/重启/停止apache服务器
  • 原文地址:https://www.cnblogs.com/adhehe/p/9686840.html
Copyright © 2011-2022 走看看