【JS】面向对象-类继承

类继承:
        - 可以实现一个类扩展另一个类
        - 使用extends关键字进行类继承
            class Child extends Parent
       
        1. extends内部机制
           - 使用原型机制,
      Child.prototype.[[Prototype]] = Parent.prototype  用于继承常规方法和属性
      Child.[[prototype]] = Parent 用于继承静态方法和属性
         - 子类可以访问父类的方法
class Animal{
    constructor(name){
        this.name = name
    }
    run(){
        console.log('animal run');
    }
}

class Dog extends Animal{

}

let d = new Dog('dog')
d.run() // 'animal run'
        2.方法重写
            2.1 完全重写
            
class Animal{
    constructor(name){
        this.name = name
    }
    run(){
        console.log('animal run');
    }
}

class Dog extends Animal{
    run(){
        console.log('dog run');
    }
}

let d = new Dog('dog')
d.run() // 'dog run'
            2.2 方法扩展
                - 当不希望完全重写父类方法时,可以使用super关键字,执行super.method()来调用一个父类的方法
class Animal{
    constructor(name){
        this.name = name
    }
    run(){
        console.log('animal run');
    }
}
class Dog extends Animal{
    run(){
        super.run()
        console.log('dog run');
    }
}
let d = new Dog('dog')
d.run() // 'animal run' 'dog run'
        * 箭头函数没有super
 
        3. constructor重写
            - 若子类不写自己的constructor,则默认会有一个空的constructor,里面有super(),也就是调用了父类的constructor,并将所有参数传了进去。
            类似这样:
            class Dog extends Animal{
                constructor(...args){
                    super(...args)
                }
            }
 
   - 执行super(...args)可以调用父类的constructor
 
           - 若子类写了constructor,需要在this语句前调用super(),否则会报错:
            Must call super constructor in derived class before accessing 'this' or returning from derived constructor
            在访问“this”或从派生构造函数返回之前,必须在派生类中调用super
 
            原因:
                JS中继承类的构造函数中有一个特殊的属性[[Constructorkind]]:"derived",这个内部标签会影响它的new行为,让继承类中的constructor无法正常工作,普通函数使用new时,会创建一个空对象并将this指定为该对象,但在这里继承类使用new时,类中的constructor不会这样做,而是期待父类中的constructor来完成这项操作。

 

        4.类字段重写
            父类构造器中只会使用自己字段的值,而不是重写的
            原因:
                类字段初始化的顺序不一样
                - 对于基类,在调用构造函数前初始化
                - 对于派生类,则是在调用完super()后才进行初始化
           
                所以,new Dog()后要执行Dog中constructor函数,所以会先执行super调用父类中的constructor。执行父类的constructor时,父类中字段已经初始化好了,但子类还没有,所以使用时会使用父类中的字段。

 

        5.super原理
            当一个对象方法执行时,它会将当前对象作为 this。随后如果我们调用 super.method(),那么引擎需要从当前对象的原型中获取 method。但仅仅依靠this无法实现,有时this指向的对象不是我们想要的。

 

            JavaScript中在函数中有一个特殊的内部属性[[HomeObject]],当一个函数被定义为类或对象中的方法时,该函数中的[[HomeObject]]属性就成为该对象。然后super就使用它接卸父原型及其方法。
const person = {
    name: 'person',
    say(){
        console.log(this.name,'say');
    }
}

const man = {
    __proto__: person,
    name:'man',
    say(){ // man.say.[[HomeObject]] == man
        super.say()
    }
}
man.say()
            在 JavaScript 语言中 [[HomeObject]] 仅被用于 super。所以,如果一个方法不使用 super,那么我们仍然可以视它为自由的并且可在对象之间复制。
           
const person = {
    name: 'person',
    say(){
        console.log(this.name,'person say');
    }
}

const man = {
    __proto__: person,
    name:'man',
    say(){ // man.say.[[HomeObject]] == man
        super.say()
    }
}
man.say()

const animal = {
    name:'animal',
    say(){
        console.log(this.name,'animal say');
    }
}

const cat = {
    __proto__: animal,
    name:'cat',
    say: man.say
}

cat.say() //cat person say 
 
            cat中的say: man.say == { // man.say.[[HomeObject]] == man
                    super.say()
                }
            由于有super,所以会找[[HomeObject]],此时的[[HomeObject]]是man,因为是在man中创建的,[[HomeObject]]已经被永久绑定了。所以会找man对象原型上的say方法。
 
            [[HomeObject]] 是为类和普通对象中的方法定义的。但是对于对象而言,方法必须确切指定为 method(),而不是 "method: function()"。
posted @ 2022-10-28 22:09  Ahoge的笔记本  阅读(172)  评论(0编辑  收藏  举报