目录
- 原型链与继承
- 继承中的原型链
- class
- super与this
我们可能会对一个问题感到好奇:为什么在派生类中,我们需要在调用this之前调用super。我们通常将其视为一种规范,却很少深入探究这个规范的真正意义。许多人认为super不过是ES6之前继承方式的语法糖。但实际上,这种看法是不准确的。要真正理解super的必要性,我们必须探究JavaScript中继承机制的内部工作原理
原型链与继承
在JavaScript中,继承的实现本质上是通过原型链的查找机制来完成的。这个过程涉及将一个类(如Person)的实例作为另一个类(如Student)的原型。这样,Student类实例就能够访问Person类的属性和方法,从而实现继承
class Person {
}
class Student extends Person {
}
const p1 = new Student()
const p2 = new Person()
console.log(p1)
console.log(p2)
我们观察一下p1和p2的原型链
我们先来观察一下p2的原型链关系,因为p2是直接通过new Person
得到的,Person是基类,所以不存在继承关系,p2.__proto__
直接指向了Person.prototype
,Person.prototype.__proto__
直接指向了Object.prototype
,Object.prototype.__proto__
直接指向了null
继承中的原型链
再看看p1的原型链,因为p1
通过new Student
得到,,所以p1.__proto__
为Student.prototype
,而Student
继承了Person
,所以Student.prototype.__proto__
指向了Person.prototype
粗看似乎和Person的原型链并没有什么不同,但仔细观察就会发现Student.__proto__
指向了Person自身,即指向了Person.prototype.constructor
,这一点似乎与我们之前在ES5实现的继承并不相同
class
那么,为什么class中的继承与ES5中的继承不一致呢
我们知道,在class中如果使用了extends就需要在constructor中调用super方法,而且必须在派生类构造方法返回前且访问this前调用
在面向对象编程语言中,继承机制通常首先确保父类的构造函数被正确调用以初始化this对象,然后这个this对象会被传递给子类。这种做法被认为是更安全的
相比之下,在ES5及之前的JavaScript版本中,继承可以通过借用构造函数或组合寄生继承实现,这些方式允许开发者选择是先调用父类构造函数还是子类构造函数。这种灵活性使得JavaScript在ES6引入class关键字之前,虽然具有对象构造的能力,但并不完全符合传统意义上的面向对象编程语言的规范
super与this
那么回到我们最开始的问题 ,为什么派生类在调用super前不能访问this呢
我们可以认为,在ES6中,ECMA委员会对类的继承机制进行了调整,从直接由子类发出this给父类的情况变为了从父类发出this并依次传递到子类,我们可以合理认为super本身是有返回值的,而这个返回值就是this,所以我们可以进一步得出子类的this是由父类得到的,所以在访问this前必须调用super
let obj = null
class Person {
constructor() {
this.name = "person"
}
getName() {
return this.name
}
}
class Student extends Person {
constructor() {
obj = super()
console.log(obj === this)
}
}
const p1 = new Student()
console.log(p1 === obj)