JavaScript继承之ES6的extends
例子:
class A { constructor(name, age) { this.name = name; this.age = age; } getName() { return this.name; } } class B extends A { constructor(name, age) { super(name, age); this.job = "IT"; } getJob() { return this.job; } getNameAndJob() { return super.getName() + this.job; } } var b = new B("Tom", 20); console.log(b.name); console.log(b.age); console.log(b.getName()); console.log(b.getJob()); console.log(b.getNameAndJob()); //输出:Tom,20,Tom,IT,TomIT
重点解析之extends关键字
上面代码定义了一个B类(class),该类通过extends关键字,继承了A类的所有属性和方法。A类中的所有方法默认是添加到B的原型上,所以extends继承的实质仍然是原型链。
测试代码:
console.log("constructor" in b); console.log("getName" in b); console.log(b.hasOwnProperty("getName")); console.log(b.hasOwnProperty("constructor")); //输出:true,true,false,false
重点解析之super关键字
super这个关键字,既可以当作函数使用,也可以当作对象使用。当作函数使用时,super代表父类的构造函数,并在子类中执行Parent.apply(this),从而将父类实例对象的属性和方法,添加到子类的this上面。以下三点需要特别注意:
1、子类必须在constructor方法中调用super方法,如果子类没有定义constructor方法,constructor方法以及其内部的super方法会被默认添加。
测试代码:
class A { constructor(name, age) { this.name = name; this.age = age; } getName() { return this.name; } } class B extends A {} var b = new B("Tom", 20); console.log(b.name); console.log(b.age); console.log(b.getName()); console.log(b.hasOwnProperty("name")); //输出:Tom,20,Tom,true
2、在子类的constructor方法中,只有调用super之后,才可以使用this关键字,否则会报错。
测试代码:
class A { constructor(name, age) { this.name = name; this.age = age; } } class B extends A { constructor(name, age) { this.job = "IT"; super(name, age); } } var b = new B("Tom", 20) //输出:报错
3、super()只能用在子类的constructor方法之中,用在其他地方就会报错。
测试代码:
class A { constructor(name, age) { this.name = name; this.age = age; } } class B extends A { toStr(name, age) { super(name, age) } } var b = new B("Tom", 20) //输出:报错
super作为对象时,在子类中指向父类的原型对象。
即super=Parent.prototype。
测试代码:
class A { constructor(name, age) { this.name = name; this.age = age; } getName() { console.log(this.name); } } A.prototype.n = 2; class B extends A { constructor(name, age) { super(name, age); } toStr() { return super.n; } activeGetName() { super.getName(); } } var b = new B("Tom", 20); console.log(b.toStr()); console.log(b.activeGetName()); //输出:2,Tom
重点解析之静态方法的继承
在一个方法前加上关键字static,就表示该方法不会被实例继承,但是父类的静态方法,会被子类继承。
例子:
class A { static say() { console.log("hello"); } } class B extends A {} console.log(B.say()); //输出:hello
也可以使用super在子类的静态方法中调用父类的静态方法。super在静态方法中指向父类本身,而不是父类的原型对象。
例子:
class A { static say() { console.log("hello"); } } class B extends A { static toStr() { super.say(); } } var b = new B(); console.log(B.toStr()); //输出:hello
继承表达式的类
类不但可以继承自其他类,也可以继承表达式。只要表达式可以被解析为一个函数并且通过new关键字可以创建新的实例对象即可。
例子1:
继承传统形式的构造函数
let Obj = function(name) { this.name = name; } Obj.prototype.getName = function() { console.log(this.name); } class Person extends Obj { constructor(name, age) { super(name); this.age = age; } } const p = new Person("Tom", 19); console.log(p.name); //输出:Tom console.log(p.age); //输出:19 p.getName(); //输出:Tom
例子2:
继承函数返回结果
let fn = function() { return class Person { constructor(name) { return { name } } } } class SomeOne extends fn() { constructor(name) { super(name); } } let p = new SomeOne("Tom"); console.log(p.name); //输出:Tom
New.target
我们知道,函数内部有一个new.target对象用于判断函数是否通过new关键字调用。类构造函数也可以通过new.target来确定类的调用形式。
例子:
class Obj { //new Obj()时,new.target的值为 Obj constructor() { if (new.target === Obj) { console.log("不可以直接调用基类!"); } } fn() { console.log(this.name); } } class Person extends Obj { //new Person("Tom")时,new.target的值为 Person constructor(name) { super(); this.name = name; } } let p1 = new Person("Tom"); p1.fn(); //输出:Tom let p2 = new Obj(); //输出:不可以直接调用基类!
*因为类必须通过new关键字调用,所以在类的构造函数中new.target的值永远不会是undefined。