继承基础
使用extends
关键字就可以继承任何拥有[[Construct]]
和原型的对象
不仅可以继承一个类,也可以继承普通的构造函数(保持向后兼容)【任何可以解析为一个类或者一个构造函数的表达式都是有效的】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Person {}class Women extends Person {}let w = new Women (); console .log (w instanceof Women ); console .log (w instanceof Person ); function Person ( ) {}class Men extends Person {}let m = new Men ();console .log (m instanceof Men ); console .log (m instanceof Person );
派生类可以通过原型链访问到类上的方法和原型上定义的方法,this的值会反映调用相应方法的类或者实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Person { sayHelloPorototype (name ) { console .log (`${name} say hello, ${this } ` ); } static sayHelloClass (name ) { console .log (`${name} say hello, ${this } ` ); } } class Women extends Person {}let p1 = new Person ();let w1 = new Women ();p1.sayHelloPorototype ('Katrina' ); w1.sayHelloPorototype ('Jack' ); Person .sayHelloClass ('Katrina1' ); Women .sayHelloClass ('Jack1' );
1 2 3 4 5 6 7 8 9 10 11 class Person { sayHelloPorototype (name ) { console .log (`${name} say hello, ${this } ` ); } static sayHelloClass (name ) { console .log (`${name} say hello, ${this } ` ); } } let Women = class extends Person {}
构造函数、HomeObject和super()
派生类可以通过super
关键字引用它们的原型
构造函数: 在类构造函数中使用super
可以调用父类构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Vehicle { constructor ( ) { this .hasEngie = true ; } } class Bus extends Vehicle { constructor ( ) { super (); console .log (this instanceof Vehicle ); console .log (this ); } } new Bus ();
静态方法: 在静态方法中可以通过super 调用继承的类上定义的静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13 class Vehicle { static identify ( ) { console .log ('vehicle' ); } } class Bus extends Vehicle { static identify ( ) { super .identify (); } } Bus .identify ();
使用super时要注意的几个问题
1 2 3 4 5 6 class Vehicle { constructor ( ) { super (); } }
不能单独调用super关键词,要么用它调用构造函数,要么用它引用静态方法
1 2 3 4 5 6 7 class Vehicle {} class Bus extends Vehicle { constructor ( ) { console .log (super ); } }
调用super()会调用父类构造函数,并将返回的实例赋值给this(也就是this指向实例)
1 2 3 4 5 6 7 8 9 10 11 class Vehicle {}class Bus extends Vehicle { constructor ( ) { super (); console .log (this instanceof Vehicle ); console .log (this ); } } new Bus ();
super()的行为如同调用构造函数,如果需要给父类构造函数传参,则需要手动传入
1 2 3 4 5 6 7 8 9 10 11 12 13 class Vehicle { constructor (id ) { this .id = id; } } class Bus extends Vehicle { constructor (id ) { super (id); } } console .log (new Bus (1234 ).id );
如果没有定义派生类构造函数,在实例化派生类时会调用super(),而且会传入所有传给派生类的参数
1 2 3 4 5 6 7 8 9 class Vehicle { constructor (id ) { this .id = id; } } class Bus extends Vehicle {}console .log (new Bus (1234 ).id );
在类构造函数中,不能在调用super()之前引用this
1 2 3 4 5 6 7 8 9 10 class Vehicle {}class Bus extends Vehicle { constructor ( ) { console .log (this ); } } new Bus ();
如果在派生类中显式定义了构造函数,则要么必须在其中调用super(),要么必须在其中返回一个对象
1 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 class Vehicle {}class Car extends Vehicle {}class Bus extends Vehicle { constructor ( ) { super (); } } class Van extends Vehicle { constructor ( ) { return {}; } } class Jeep extends Vehicle { constructor ( ) { name : 'Katrina' } } console .log (new Car ()); console .log (new Bus ()); console .log (new Van ()); console .log (new Jeep ());
抽象基类
抽象基类:可以供其他类继承,但是本身不会实例化
可以通过判断new.target来阻止实例化
补课:new.target
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Vehicle { constructor ( ) { console .log (new .target ); if (new .target === Vehicle ) { throw new Error ('Vehicle cannot be directly instantiated' ); } } } class Bus extends Vehicle {} new Bus (); new Vehicle ();
通过在抽象基类构造函数中进行检查,可以要求派生类必须定义某个方法
1 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 class Vehicle { constructor ( ) { console .log (new .target ); if (new .target === Vehicle ) { throw new Error ('Vehicle cannot be directly instantiated' ); } if (!this .foo ) { throw new Error ('Inheriting class must define foo()' ); }; console .log ('success' ); } } class Bus extends Vehicle { foo ( ) {} } class Van extends Vehicle {}new Bus (); new Van ();
基础内置类型
类继承内置引用类型方便扩展内置类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class SuperArray extends Array { shuffle ( ) { for (let i = this .length - 1 ; i >= 0 ; i--) { const j = Math .floor (Math .random () * (i+1 )); [this [i], this [j]] = [this [j], this [i]]; } } } let a = new SuperArray (1 ,2 ,3 ,4 ,5 );console .log (a instanceof SuperArray ); console .log (a instanceof Array ); console .log (a); a.shuffle (); console .log (a);
有些内置类型的方法会返回新实例,默认情况下,返回实例的类型与原始实例的类型是一致的
1 2 3 4 5 6 7 8 9 class SuperArray extends Array {}let a1 = new SuperArray (1 , 2 , 3 , 4 , 5 );let a2 = a1.filter (x => !!(x%2 ))console .log (a1); console .log (a2); console .log (a1 instanceof SuperArray ); console .log (a2 instanceof SuperArray );
如果想覆盖这个默认行为,则可以覆盖Symbol.species
访问器,这个访问器决定在创建返回的实例时使用的类
1 2 3 4 5 6 7 8 9 10 11 12 13 class SuperArray extends Array { static get [Symbol .species ]() { return Array ; } } let a1 = new SuperArray (1 , 2 , 3 , 4 , 5 );let a2 = a1.filter (x => !!(x%2 ))console .log (a1); console .log (a2); console .log (a1 instanceof SuperArray ); console .log (a2 instanceof SuperArray );
类混入
类混入:把不同类的行为集中到一个类
混入模式可以通过在一个表达式中连缀多个混入元素来实现,这个表达式最终会解析为一个可以被继承的类
假设现在有一个需求:如果Person
类需要组合A
、B
、C
,则需要某种机制实现B
继承A
,C
继承B
,而Person
再继承C
,从而把A
、B
、`C 组合到这个超类中
实现策略:定义一组“可嵌套”的函数,每个函数分别接收一个超类作为参数,而将混入类定义为这个参数的子类,并返回这个类,这些组合函数可以连缀调用,最终组合成超类表达式
1 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 class Vehicle {}let FooMixin = (Superclass ) => class extends Superclass { foo ( ) { console .log ('foo' ); } }; let BarMixin = (Superclass ) => class extends Superclass { bar ( ) { console .log ('bar' ); } }; let BazMixin = (Superclass ) => class extends Superclass { baz ( ) { console .log ('baz' ); } }; class Bus extends FooMixin (BarMixin (BazMixin (Vehicle ))) {}let b = new Bus ();b.foo (); b.bar (); b.baz ();
通过写一个辅助函数,可以把嵌套展开
1 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 class Vehicle {}let FooMixin = (Superclass ) => class extends Superclass { foo ( ) { console .log ('foo' ); } }; let BarMixin = (Superclass ) => class extends Superclass { bar ( ) { console .log ('bar' ); } }; let BazMixin = (Superclass ) => class extends Superclass { baz ( ) { console .log ('baz' ); } }; function mix (BassClass, ...Minins ) { return Minins .reduce ((prev, curr ) => curr (prev), BassClass ) } class Bus extends mix (Vehicle , FooMixin , BarMixin , BazMixin );let b = new Bus ();b.foo (); b.bar (); b.baz ();