# Class的继承
# 简介
不同于ES5利用原型实现继承的方式,ES6利用
extends关键字实现继承。其实是寄生组合继承的语法糖
class ColorPoint extends Point { // extends 相当于Son.prototype = Object.create(Father.prototype)
    constructor(x, y, color) {
        super(x, y); // 调用父类的constructor(x, y)  相当于Father.call(this)
        this.color = color;
    }
    toString() {
        return this.color + ' ' + super.toString(); // 调用父类的toString() super当作对象来使用
    }
}
 2
3
4
5
6
7
8
9
10
这个例子就是ColorPoint类继承Point类。
TIP
- 子类只要继承父类,可以不写
constructor,一旦写了第一句就是super 
class Person {
    constructor() {
        this.type = 'person'
    }
}
class Student extends Person {
    constructor() {
        super()
    }
}
var student1 = new Student() 
var person1 = new Person()
console.log(student1.type) // person
console.log(student1 instanceof Student) // true
console.log(student1 instanceof Person) // true
console.log(student1.hasOwnProperty('type')) // true
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# super关键字
 - 在上面的例子中
super关键字表示父类的构造函数,新建父类的this对象。相当于Point.prototype.constructor.call(this) - 子类没有
this对象,所以必须在子类的构造方法中调用super方法,达到继承父类this对象的作用。 - 在子类的构造函数中,只有调用
super之后才可以使用this关键字,否则会报错。 - 这个关键字可以当对象使用,也可以当函数使用,两种使用效果截然不同。第一点就是作为函数使用的情况;
 super()内部的this指向的是子类。作为函数时,super()只能用在子类的构造函数中,用在其他地方会报错。super作为对象时在普通方法中指向父类的原型对象;在静态方法中指向父类。
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); // super指向父类
    }
    myMethod(msg) {
        super.myMethod(msg); //super指向父类原型对象
    }
}
Child.myMethod(1) // staticc 1
var child = new Child();
child.myMethod(2); // instance 2
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 由于
super作为对象使用时指向父类的原型对象,所以定义在父类实例上的方法或属性是无法通过super调用的,即构造方法内的。如果定义在父类的原型对象上,就可以取到 
class A {
    constructor() {
        this.p = 2;
    }
}
class B extends A {
    get m () {
        return super.p;
    }
}
let b = new B();
b.m // undefined
 2
3
4
5
6
7
8
9
10
11
12
例子二:
class A {
    constructor() {
        this.x = 1;
    }
}
class B extends A {
    constructor() {
        super();
        this.x = 2;
        super.x = 3; // 读取super.x时,相当于读取A.prototype.x,所以返回undefined
        console.log(super.x); // undefined
        console.log(this.x); // 3
    }
}
let b = new B()
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
由于绑定子类的
this,因此如果通过super对象某个属性赋值,这时super就是this,赋值的属性变成子类实例的属性。
# ES5继承和ES6继承的区别
ES5的继承实质是先创造子类的实例对象
this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6则是先创造父类的实例对象this(使用super),然后再用子类的构造函数修改this。
# 类的prototype属性和__proto__属性
 Class作为构造函数的语法糖,同时有
prototype和__proto__属性,因此同时存在两条继承链:
- 子类的
__proto__属性表示构造函数的继承,总是指向父类 - 子类的
prototype属性的__proto__属性表示方法的继承,总是指向父类的prototype属性。 
class A {}
class B extends A {}
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
 2
3
4
造成上述两条继承链的原因是因为类的继承实现模式如下:
class A {}
class B {}
// B的实例继承A的实例
Object.setPrototypeOf(B.prototype,A.prototype);
// B的实例继承A的静态属性
Object.setPrototype(B, A);
const b = new B();
 2
3
4
5
6
7
8
9
这两条继承链可以这样理解:作为一个对象,子类(B)的原型(
__proto__属性)是父类A;作为一个构造函数,子类B的原型(prototype属性)是父类的实例。
# extends继承目标的特殊性
 - 子类继承
Object类。 
class A extends Object {
}
A.__proto__ === Object // true
A.prototype.__proto__ === Object.prototype // true
 2
3
4
5
这种情况A其实就是构造函数
Object的复制,A的实例就是Object的实例。
- 不存在任何继承。
 
class A {
}
A.__proto__ === Function.prototype // true
A.prototype.__proto === Object.prototype // true
 2
3
4
5
A作为一个基类(不存在任何继承)就是一个普通函数,所以直接继承
Function.prototype。但是A调用后返回一个空对象(即Object的实例)。
- 子类继承
null。 
class A extends null {
}
A.__proto__ === Function.prototype // true
A.prototype.__proto__ === undefined // true
 2
3
4
5
和第二种情况类似,只是A调用后返回的对象不继承任何方法,实际相当于执行了:
class C extends null {
    constructor() {
        return Object.create(null)
    }
}
 2
3
4
5
# 实例的__proto__属性
 子类实例的
__proto__属性的__proto__属性指向父类实例的__proto__属性,也就是说子类的原型的原型是父类的原型。
# 原生构造函数的继承
TIP
ES6允许继承原生构造函数定义子类,因为ES6先创建父类的实例对象this,然后再用子类的构造函数修饰this,使得父类的所有行为都可以继承。所以extends也可以用来继承原生的构造函数
class MyArray extends Array {
    constructor(...args) {
        super(...args)
    }
}
var arr = new MyArray();
arr[0] = 12;
arr.length // 1
arr.length = 0;
arr[0] // undefined
var brr = new MyArray(1,2,3)
console.log(brr) // [1,2,3]
 2
3
4
5
6
7
8
9
10
11
12
13
# ES6继承转码为ES5
class Person {
    constructor() {
        this.type = 'person'
    }
}
class Student extends Person {
    constructor() {
        super()
    }
}
 2
3
4
5
6
7
8
9
10
11
利用babel在线转码结果:
"use strict";
function _typeof(obj) { "@babel/helpers - typeof"; 
    if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { 
        _typeof = function _typeof(obj) { 
            return typeof obj; 
        }; 
    } else { 
        _typeof = function _typeof(obj) { 
            return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; 
        }; 
    } 
    return _typeof(obj); 
}
function _inherits(subClass, superClass) { 
    if (typeof superClass !== "function" && superClass !== null) { 
        throw new TypeError("Super expression must either be null or a function"); 
    } 
    subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); 
    if (superClass) _setPrototypeOf(subClass, superClass); 
}
function _setPrototypeOf(o, p) { 
    _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { 
        o.__proto__ = p; return o; 
    }; 
    return _setPrototypeOf(o, p); 
}
function _createSuper(Derived) { 
    var hasNativeReflectConstruct = _isNativeReflectConstruct(); 
    return function _createSuperInternal() { 
        var Super = _getPrototypeOf(Derived), result; 
        if (hasNativeReflectConstruct) { 
            var NewTarget = _getPrototypeOf(this).constructor; 
            result = Reflect.construct(Super, arguments, NewTarget); 
        } else { 
            result = Super.apply(this, arguments); 
        } 
        return _possibleConstructorReturn(this, result); 
    }; 
}
function _possibleConstructorReturn(self, call) { 
    if (call && (_typeof(call) === "object" || typeof call === "function")) { 
        return call; 
    } 
    return _assertThisInitialized(self); 
}
function _assertThisInitialized(self) { 
    if (self === void 0) { 
        throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); 
    } 
    return self; 
}
function _isNativeReflectConstruct() { 
    if (typeof Reflect === "undefined" || !Reflect.construct) return false; 
    if (Reflect.construct.sham) return false; 
    if (typeof Proxy === "function") return true; 
    try { 
        Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; 
    } catch (e) { 
        return false; 
    } 
}
function _getPrototypeOf(o) { 
    _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { 
        return o.__proto__ || Object.getPrototypeOf(o); 
    }; 
    return _getPrototypeOf(o); 
}
function _instanceof(left, right) { 
    if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { 
        return !!right[Symbol.hasInstance](left); 
    } else { 
        return left instanceof right; 
    } 
}
function _classCallCheck(instance, Constructor) { 
    if (!_instanceof(instance, Constructor)) { 
        throw new TypeError("Cannot call a class as a function"); 
    } 
}
var Person = function Person() {
  _classCallCheck(this, Person);
  this.type = 'person';
};
var Student = /*#__PURE__*/function (_Person) {
  _inherits(Student, _Person);
  var _super = _createSuper(Student);
  function Student() {
    _classCallCheck(this, Student);
    return _super.call(this);
  }
  return Student;
}(Person);
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
TIP
Student函数为一个自执行函数,接收一个Person参数(就是要继承的父类)。返回一个构造函数Student。