简介
Class 可以通过 extends 关键字实现继承,比Es5通过修改原型链实现继承,更清晰和方便。
class Point {}class ColorPoint extends Point {}
ColorPoint 通过extends 继承了 point 的所有方法和属性。两个类一样。
class ColorPoint extends Point {constructor(x, y, color) {super(x, y)this.color = color}toString() {return this.color + ' ' + super.toString() // 调用父类的toString()}}
super 关键字 表示父类的构造函数,用来新建父类的 this 对象。
子类必须在contructor 方法中调用super 方法,否则新建实例时会报错。
子类的this 对象,必须先通过父类的构造函数完成塑造,得到父类同样的实例属性和方法,然后再对其加上自己的实例属性和方法。如果不调用super 方法,子类就不能得到this 对象。
class Point {}class ColorPoint extends Point {constructor() {}}let cp = new ColorPoint() // 报错
ColorPoint 继承了父类Point, 但它的构造函数没有调用 super 方法,导致新建实例时报错。
Es6 继承机制:
先将父类实例对象的属性和方法,加到this 上面(必须先调用super方法),然后再用子类的构造函数修改this.
如果子类没有定义constructor 方法,这个方法会被默认添加。
class ColorPoint extends Point {}// 等同于class ColorPoint exntends Point {construcotr(...args) {super(...args)}}
注意: 在子类的构造函数中,只有调用super 方法后,才可以使用 this 关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有super 方法才能调用父类实例。
class Point {constructor(x, y) {this.x = xthis.y = y}}class ColorPoint extends Point {constructor(x, y, color) {this.color = color // 报错super(x, y)this.color = color // 正确}}
子类的constructor 方法this 关键字,必须要在super() 调用后使用。
let cp = new ColorPoint(25, 8, 'green')cp instanceof ColorPoint // truecp instanceof Point // true
cp 同时是ColorPoint和Point 两个类的实例。
父类的静态方法也会被子类继承
class A {static hello() {console.log('hello world')}}class B extends A {}B.hello() // 'hello world'
Object.getPrototypeOf()
Object.getPrototypeOf 方法可从子类获取父类
Object.getPrototypeOf(ColorPoint) === point // true
用这个方法判断: 一个类是否继承了另一个类。
super 关键字
可以当函数使用,也可以当对象使用。
super =》函数
super 方法,代表父类的构造函数。Es6 要求,子类的构造函数必须执行一次super函数。
class A {}class B extends A {construcor() {super()}}
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() // Anew B() // B
new.target 指向当前正执行的函数。所以,supe() 执行时,指向的是子类B的构造函数,而不是父类A的构造函数。
作为函数时,super() 只能用在子类的构造函数中,用在其它地方会报错。
class A {}class B extens A {m() {super() // 报错}}
super() 用在B类的m方法中,就会导致语句报错。
super 作为对象
super 作为对象时,
在普通方法中,指向父类的原型对象;
在静态方法中, 指向父类。
class A {p() {return 2}}class B extens A {constructor() {super()console.log(super.p()); // 2}}let b = new B()
子类B 中普通方法的 super,指向父类A.prototype, 所以 super.p() 相当于A.prototype.p()
由于super 指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super 调用的。
calss 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 就可以取到。
calss A {}A.prototype.x = 2class B extends A {get m() {return super.x;}}let b = new B()b.m // 2
属性x 定义在A.prototype 上, 所以 super.x 可以取到它的值。
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.portotype.print() 内部的this 指向子类的B实例,导致输出的时2,而不是1.实际上执行的是 super.print.call(this)
由于this 指向子类实例,所以如果 super() 对某个属性赋值,这时super 就是 this, 赋值的属性会变成子类实例属性。
clas A {constructor() {this.x = 1}}class B extens A {constructor() {super()this.x = 2super.x = 3console.log(super.x) // undefinedconsole.log(this.x) // 3}}let b = new B()
supper.x = 3, 等同于 this.x = 3. 当读取super.x 时,读取的是 A.prototype.x, 因为 A 的原型上没有x 属性,返回undefined.
如果super 作为对象,用在静态方法中,这时 super 指向父类,而不是父类的原型对象。
class Parent {static myMethod(msg) {console.log('static', msg)}myMethod(msg) {console.log('instance', msg)}}class Child extends Parent {static myMethod(msg) {supe.myMethod(msg)}myMethod(msg) {super.myMethod(msg)}}Child.myMethod(1) // static 1let 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 = 3B.m() // 3
静态方法 B.m 中, super.print 指向父类的静态方法。这个方法里 this 指向的是 B,而不是B的实例。
注意: 使用super 时, 必须显示指定是作为函数、还是作为对象使用,否则会报错。
class A {}class B extends A {constructor() {super()console.log(super); // 报错console.log(super.valueof() instanceof B); // true}}let b = new B()
console.log(super)中的super,无法看出是作为函数使用,还是作为对象使用,会报错。
super.valueOf() 表明super是一个对象,因此不会报错。由于 super使得this指向B的实例,所以super.valueOf() 返回的是一个B的实例。
由于对象总是继承其它对象的,所以可以在任意一个对象中,使用super 关键字。
let obj = {toString() {return 'MyObject:' + super.toString()}}obj.toString(); // MyObject: [Object Object]
类的 prototype 属性和 proto 属性
Class 同时有 prototype 属性和 proto属性,因此同时存在两条继承链。
子类的proto 属性,表示构造函数的继承,总是指向父类。
子类prototype属性的proto 属性,表示方法的继承,总是指向父类的prototype 属性。
class A {}class B extends A {}B.__proto__ === A; // trueB.prototoype.__proto__ === A.prototype
类的继承是按照下面的模式实现的。
class A {}class B {}// B 的实例继承 A的实例Object.setPrototypeOf(B.prototype, A.prototype)// B 继承 A 的静态属性Object.setPrototype(B, A)
Object.setPrototypeOf 方法的实现
Object.setPrototypeOf = function(obj, proto) {obj.__proto__ = protoreturn obj}
