es6中用class和extends关键字来实现继承。es6中引入了class关键字来声明类, 而class(类)可通过extends关键字实现继承,让子类继承父类的属性和方法,语法“class 父类名{...} class 子类名 extends 父类名{...};”。
本教程操作环境:windows7系统、ecmascript 6版、dell g3电脑。
es6中可利用class关键字配合extends关键字来实现继承。
es6 class 的继承1.简介
class可以通过extends关键字实现继承,让子类继承父类的属性和方法。这比 es5 的通过修改原型链实现继承,要清晰和方便很多。
//父类class point { ...}//子类class colorpoint extends point { constructor(x, y, color) { super(x, y); this.color = color; } tostring() { return this.color + '' + super.tostring(); // 调用父类的tostring方法 }}
上面代码中,constructor方法和tostring方法内部,都出现了super关键字,super在这里表示父类的构造函数,用来新建一个父类的实例对象。
es6规定,子类必须在constructor方法中调用super(),否则会报错,这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后在添加子类自己的实例属性和方法。
为什么子类的构造函数,一定要调用super()?这是因为在es5的继承机制中,是先创造一个独立的子类的实例对象,然后再将父类的方法添加到这个对象上,即“实例在前,继承在后”;es6的继承机制,则是先将父类的属性和方法,加到一个空的对象上面,然后再将该对象作为子类的实例,即“继承在前,实例在后”。
这意味着,每次新建子类实例时,父类的构造函数必定会先运行一次
class foo { constructor() { console.log(1); }}class bar extends foo { constructor() { super(); console.log(2); }}const bar = new bar(); // 1 2
上面的代码中,子类bar新建实例时,会输出1和2,这就是因子类构造函数调用super()时,会执行一次父类构造函数。只有在子类的构造函数中调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,必须先完成父类的继承,只有super方法才能让子类实例继承父类。
class point { constructor(x, y) { this.x = x; this.y = y; }}class colorpoint extends point { constructor(x, y, color) { this.color = color; super(x, y); this.color = color; }}"
如果子类没有定义constructor方法,这个方法会默认添加,并且里面会调用super,也就是说,不管有没有显示定义,任何一个子类都有constructor方法.
class point { constructor(x, y) { this.x = x; this.y = y; }}class colorpoint extends point {}let cp = new colorpoint(25, 8);console.log(cp); //{x: 25, y: 8}class colorpoint extends point { constructor(...args) { super(...args); }}let cp = new colorpoint(25, 8);console.log(cp); //{x: 25, y: 8}
2.私有属性和私有方法的继承
父类所有的属性和方法,都会被子类继承,除了私有的属性和方法。子类无法继承父类的私有属性,或者说私有属性只能在定义它的class里面使用。
class foo { #p = 1; #m() { console.log('hello'); }}class bar extends foo { constructor() { super(); console.log(this.#p); // 报错 this.#m(); // 报错 }}
上面示例中,子类 bar 调用父类 foo 的私有属性或私有方法,都会报错。
如果父类定义了私有属性的读写方法,子类就可以通过这些方法,读写私有属性。
class foo { #p = 1; getp() { return this.#p; }}class bar extends foo { constructor() { super(); console.log(this.getp()); // 1 }}
3.静态属性和方法的继承
父类的静态属性和静态方法,也会被子类继承。
class a { static hello() { console.log('hello world'); }}class b extends a {}b.hello() // hello world
上面代码中,hello()是a类的静态方法,b继承a,也继承了a的静态方法。
注意,静态属性是通过浅拷贝实现继承的,如果继承的属性是原始数据类型,子类中操作继承的静态属性不会影响到父类,但如果继承的属性是一个对象,那么子类修改这个属性会印象到父类
class c { static foo = 100;}class d extends c { constructor() { super(); d.foo--; }}const d = new d();c.foo; // 100d.foo; // 99class a { static foo = { n: 100 };}class b extends a { constructor() { super(); b.foo.n--; }}const b = new b();b.foo.n // 99a.foo.n // 99
4.object.getprototypeof()
object.getprototypeof()方法可以用来从子类上获取父类。
class point { /*...*/ }class colorpoint extends point { /*...*/ }object.getprototypeof(colorpoint) === point// true
因此,可以使用这个方法判断,一个类是否继承了另一个类。
5.super关键字
super关键字既可以当做函数使用,也可以当做对象使用
第一种情况,super作为函数调用时,代表父类的构造函数。调用super的作用是形成子类的this对象,把父类的实例属性和方法都放到这个this对象上面。
class a { constructor() { console.log(new.target.name); }}class b extends a { constructor() { super(); }}new a(); // anew b(); // b
第二种情况,super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
class a { p() { return 2; }}class b extends a { constructor() { super(); console.log(super.p()); // 2 }}let b = new b();
上面代码中,子类b中的super.p(),将super当做一个对象使用,这时super在普通对象中,指向的是a.prototype,super.p()相当于a.prototype.p()。
由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。如下所示:
class a { constructor() { this.p = 2; }}class b extends a { get m() { return spuer.p; }}let b = new b();b.m // undefined
为了解决这种问题,可以将属性定义在父类的原型对象上
class a {};a.prototype.x = 2;class b extends a { constructor() { super(); console.log(super.x); }}let b = new b();
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(),但是此时方法内部的this指向是子类b的实例,所以输出2。
由于this指向的是子类实例,所有如果通过super对某个属性赋值,这时super就是this,赋值的属性会变成子类实例的属性
class a { constructor() { this.x = 1; }}class b extends a { constructor() { super(); this.x = 2; super.x = 3; console.log(super.x); //undefind console.log(this.x); // 3 }}
上面代码中,super.x赋值为3,这时等同于对this.x赋值为3。而当读取super.x 的时候,读的是a.prototype.x,所以返回undefined。
如果super作为对象,用在静态方法之中,这时super将指向父类,而不是父类的原型对象。
class parent { static mymethod(msg) { console.log('static', msg); } mymethod(msg) { console.log('instance', msg); }}class children extends parent { static mymethod(msg) { super.mymthod(msg); } mymethod(msg) { super.mymethod(msg); }}child.mymethod(1); // static 1var 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 = 3;b.m() // 3
在静态方法m中,super.print指向父类的静态方法,到那时this指向的是类b,而不是b的实例。
【推荐学习:javascript高级教程】
以上就是es6中用什么实现继承的详细内容。