持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,点击查看活动详情
我们知道构造函数是有继承的,而class的实质其实就是构造函数。但是class又是怎样实现继承的呢?
extends
class使用关键字extends实现的继承。
class Father {
constructor(name) {
this.name = name;
}
sayName() {
console.log('Father:'+this.name)
}
}
class Son extends Father {}
let ly = new Son('ly');
ly.sayName(); // Father: ly
我们可以发现我们并没有在Son类中创建sayName函数,但是,我们使用extends继承Father后,我们却可以使用sayName函数。
我们可以发现,ly.__ proto __ === Son.prototype, 而Son.prototype.__ proto __ === Father.prototype.
以此形成了一条原型链,即Son原型继承了Father。当我们在ly实例中使用sayName方法时,会首先在ly实例上查找有没有这个方法,没有则去ly.__ proto__ 即Son的原型上查找,如果还没有,就会继续沿着原型链,去ly.__ proto__.__ proto__即Son.prototype.__ proto__ 即Father的原型上查找,发现有这个方法,就调用这个方法。
所以在类中使用extends的本质其实等于:
function Father(name) {
this.name = name;
}
Father.prototype.sayName = function() {
console.log('Father:'+this.name)
}
function Son(name){
Father.call(this,name);
}
Son.prototype.__proto__ = Father.prototype;
let ly = new Son('ly');
ly.sayName(); // Father:ly
其实就是上一节我们总结的:原型链+盗用构造函数 == 继承
super
我们还可以看出,在Son中并没有使用类似于盗用构造函数的方法,也就是没有Father.call(this,name),那么为何还会给实例绑定上name呢?
这就不得不说一下在类中有一个关键字super了。
super关键字只能在extends中使用,也就是说,如果我们在Son中没有extends Father,那么我们使用super就会报错。
class Father {
constructor() {
super()
}
}
new Father() // Uncaught SyntaxError: 'super' keyword unexpected here
那么super的作用是什么呢?
在静态方法中,可以通过super调用继承的类上定义的方法:
class Father {
static sayHi() {
console.log('Hello')
}
}
class Son extends Father {
static sayHi() {
super.sayHi()
}
}
Son.sayHi(); // Hello
在constructor中使用super会调用父类构造函数,并将返回的实例赋值给this
class Father {}
class Son extends Father {
constructor() {
super();
console.log(this instanceof Father)
}
}
new Son(); // true
super()的行为如同调用构造函数,如果需要给父类构造函数传参,需要我们手动传参。
class Father {
constructor(name) {
this.name = name;
}
}
class Son extends Father {
constructor(name) {
super(name)
}
}
console.log(new Son('ly')); // Son {name: 'ly'}
如果我们在派生类(也就是子类)没有定义类构造函数,在实例化时会调用super(),而且会把传入的参数所有传给父类。
class Father {
constructor(name) {
this.name = name;
}
}
class Son extends Father {}
console.log(new Son('ly')); // Son {name: 'ly'}
在子类中,不能在调用super之前引用this,否则会报错
class Father {
constructor(name) {
this.name = name;
}
}
class Son extends Father {
constructor(name,age) {
this.age = age;
super(name)
}
}
console.log(new Son('ly',18));
// Uncaught ReferenceError:
// Must call super constructor in derived class before accessing 'this' or returning from derived constructor
在子类中显示的定义了constructor,则要么必须在其中调用super(),要么必须返回一个对象
class Father {}
class Son extends Father {
constructor() {
}
}
console.log(new Son());
// Uncaught ReferenceError:
// Must call super constructor in derived class before accessing 'this' or returning from derived constructor
或者:但是不推荐这个,没有啥意义,都返回一个{}了,那么作为构造函数还有啥意义???
class Father {}
class Son extends Father {
constructor() {
return {}
}
}
console.log(new Son()); // {}
所以这也就解决了为什么我们最开始在Son中没有调用父类构造函数,为啥ly实例可以有name属性。这就是以为我们在Son中没有显示定义constructor,那么js底层默认在new Son('ly')的时候就会在Son中执行super('ly')了。而在子类构造函数中使用super()就是就是等于在构造函数中使用Father.call(this),区别就是super会自动改变父类的this指向,让其指向子类。
此外,class还可以继承JS的内置类或者内置的构造函数(保持向下兼容),可以方便的扩展内置类型。