Class的继承
1.简介
Class通过extends关键字实现继承
class Shape{}
class Rect extends Shape{}
// Rect 类继承了Shape类的所有属性和方法。
class Animal {
constructor(age) {
this.age = age
}
toString(){
return `Animal`
}
}
class Duck extends Animal {
constructor(age, color) {
super(age)
this.color = color
}
toString(){
return super.toString()+' duck'
}
}
// super:表示父类的构造函数,用来新建父类的this对象。
// 子类必须在constructor方法中调用super方法,否则新建实例时会报错。
/*
原因:子类自己的this对象,必须先通过父类的构造函数完成塑造,
得到与父类同样的实例和方法,然后再加工,加上子类自己的实例属性和方法。
不调用super方法,子类就得不到this对象。
*/
// 如果子类没有定义constructor方法,constructor方法则会默认添加。
// 在子类的constructor函数中,只有调用了super之后,才可以使用this关键字。
class Animal {
constructor(age,weight) {
this.age = age
this.weight = weight
}
toString(){
return `Animal`
}
}
class Duck extends Animal {
constructor(age,weight, color) {
this.color = color
super(age,weight)
}
toString(){
return super.toString()+' duck'
}
}
// let whiteDuck = new Duck(3,20,'white') //报错。
// 子类可以继承父类的静态方法。
class Person {
static sayName(){
console.log("person")
}
}
class Student extends Person {
}
Student.sayName() // person
2.Object.getPrototypeOf()
此方法可以用来获取一个子类的父类。
Object.getPrototypeOf(Student) // ....
3.super关键字
super关键字可以当函数使用,也可以当对象使用。
super作为函数调用
此时,代表父类的构造函数,子类的构造函数必须执行一次super函数。
class A {}
class B extends A{
constructor(){
super()
}
}
super代表父类A的构造函数,但返回的是子类B的实例,即super内部的this指向的是B的实例,故在此处,super()相当于A.prototype.constructor.call(this)
class A {
constructor() {
console.log(new.target.name);
}
}
class B extends A {
constructor() {
super();
}
}
new A() // A
new B() // B
// new.target指向当前正在执行的构造函数。
作为函数时,super()只能用在子类的构造函数之中。
class A { }
class B extends A {
m() {
super() // 报错
}
}
super作为对象调用
作为对象时,在普通方法中使用,指向父类的原型对象;在静态方法中使用,指向父类。
class Person {
say(name){
console.log("hello "+name)
}
}
class Student extends Person{
constructor(name){
super()
super.say(name)
}
}
let tony = new Student('Tony') // hello Tony
// 此时,super.say(),就相当于A.prototype.say()
// 此时,定义在父类实例上的方法和属性是无法通过super调用的。
ES6规定,在子类普通方法中通过super调用父类的方法时,方法内部的this指向当前子类实例。
class Person {
constructor(){
this.name = 'unknown'
}
printName(){
console.log(this.name)
}
}
class Student extends Person {
constructor(){
super()
this.name = 'student'
}
pri(){
super.printName()
}
}
let tony = new Student()
tony.pri() // student
// super.printName(),调用的是Person.prototype.printName(),但是其内部的this指向子类的实例,输出student。相当于super.printName.call(this)
// 因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) // undefined
console.log(this.x) // 3
}
}
let c = new B()
super作为对象,用在静态方法中,此时super将指向父类,而不是父类的原型对象。
class Parent {
static parentMethod(){
console.log("static")
}
parentMethod(){
console.log("no static")
}
}
class Son extends Parent {
static sonMethod(){
super.parentMethod()
}
sonMethod(){
super.parentMethod()
}
}
Son.sonMethod() // static
let son = new Son()
son.sonMethod() // no static
在子类的静态方法中通过super调用父类的方法时,父类方法内部的this指向当前的子类,而不是子类的实例。
class A {
constructor(){
this.x = 1
}
static pri(){
console.log(this.x)
}
}
class B extends A {
constructor(){
super()
this.x = 444
}
static pri (){
super.pri()
}
}
B.x = 2
B.pri() // 2
// 在上面代码中,B的静态方法pri中,super.pri指向父类的静态方法。此方法中的this指向类B,而不是B的实例。
使用super的时候,必须显示指定是作为函数还是对象使用,否则会报错。
class Person {
}
class Student extends Person {
constructor(){
super()
console.log(super) // 报错。
}
}
对象总是继承其他对象的,所以可以在任意一个对象中,使用super关键字。
let obj ={
toString(){
return "MyObject"+ super.toString()
}
}
obj.toString()
4.类的prototype属性和__proto__属性
大多数浏览器的ES5实现时,每个对象都有一个__proto__属性,指向对应的构造函数的prototype属性。
而Class作为构造函数的语法糖,同时有prototype和__proto__两个属性,故同时存在两条继承链。
- 子类的__proto__属性,表示构造函数的继承,总是指向父类
- 子类的prototype属性的__proto__属性,表示方法的继承,指向父类的prototype属性。
class Person {}
class Student extends Person {
constructor(){
super()
}
}
console.log(Student.__proto__) // class Person {}
console.log(Student.prototype.__proto__ === Person.prototype) // true
extends 关键字后可以跟多种类型的值。
class B extends A {}
// A只要是一个具有prototype属性的函数,都可以被B继承。函数都具有prototype属性(Function.prototype函数除外),故A可以是任意函数。
// 讨论两种情况
// 1.子类继承Object类
class A extends Object{}
A.__proto__ === Object // true
A.prototype.__proto__ === Object.prototype // true
// 这种情况,A就是构造函数Object的复制,A的实例就是Object的实例。
// 2.不存在任何继承
class A {}
A.__proto__ === Function.prototype // true
A.prototype.__proto__ === Object.prototype // true
// 这种情况下,A作为一个基类(不存在任何继承),就是一个普通函数,即直接继承Function.prototype。但是,A调用后返回一个空对象(即Object实例),故A.prototype.__proto__指向构造函数(Object)的prototype属性。
实例的__proto__属性
子类实例的__proto__属性的__proto__属性指向父类市里的__proto__属性。
class Point{
constructor(x,y){
this.x = x
this.y =y
}
}
class ColorPoint extends Point{
constructor(x,y,color){
super(x,y)
this.color = color
}
}
let p1 = new Point(2,3)
let p2 = new ColorPoint(2,4,'blue')
// ColorPoint.prototype.__proto__ === Point.prototype
console.log(p2.__proto__.__proto__ === p1.__proto__) // true
// 故,通过子类实例,可以修改父类实例的属性和方法。
p2.__proto__.__proto__.pri = () => {
console.log("object")
}
p1.pri() // object