认识继承

69 阅读4分钟

认识继承

如果s1对象内部 具有一个属性为name,并且s1还可以使用init方法,那么我们就可以说Stu这个构造函数 继承自Person构造函数。Stu是Person的子类。Person是Stu的父类。

原型继承

利用自定义原型的方式实现继承关系

  • 核心:将子类的原型修改为父类的实例化对象
  • 优点:可以使用父类的属性和方法,实现了继承
  • 缺点:
  • 1.原本原型上的方法不能使用了(因为原型对象被改变了);sayHi1 是添加到原本原型上的方法,后续原型被更改后他就不能使用了;sayHi2是添加到新的原型上的方法,这个方法可以正常使用
  • 2.继承到的属性并不在自己身上,而是在原型对象上(不过不影响使用)。
//构造函数1

function Person (name) {
    this.name = name
}
Person.prototypeo.init = function ()  {
    console.log('我是 Person 原型上的方法')
}

//构造函数2
function Stu (age) {
    this.age = age
}
Stu.prototype.sayHi = function () {
    console.log('我是Stu 原型上的方法')
}
Stu.prototype = new Person('张三')
Stu.prototype.sayHi () {
//Stu的原型方法写在原型继承的后面可以解决sayHi方法不能使用的问题
    console.log('我是Stu 原型上的方法')
}
    console.log('Stu 原型对象:',Stu.prototype)
    const s1 = new Stu(18)
    console.log('Stu 的实例化对象;',s1)
    console.log(s1.name)
    s1.init()
    s1.sayHi()
    

借用构造函数继承

  • 核心:把父类构造函数当作普通函数调用,并利用call 修改这个函数内部的this 指向(如果不修改的话,函数的this指向了其他对象)。
  • 优点:把父类的属性全都继承在了自己的身上。
  • 缺点:
  • 1。只能继承父类的属性,不能继承父类的方法。
  • 2.每次调用Stu的时候,Stu内部还会自动调用一次 Person函数
//构造函数1

function Person (name) {
    this.name = name
}
Person.prototypeo.init = function ()  {
    console.log('我是 Person 原型上的方法')
}

//构造函数2
function Stu (age) {
    this.age = age
    Person.call(this,name)
}
Stu.prototype.sayHi = function ()  {
    console.log('我是Stu 原型上的方法')
}

    const s1 = new Stu(18,'张三')
    console.log(s1)
  
    s1.sayHi()
    

组合继承

  • 核心:把原型继承与借用借用构造函数继承结合起来使用。
  • 优点:实例化对象上具有继承到的属性,并且能够继承到父类原型上的方法
  • 缺点:实例化对象上与原型对象上,都有父类的属性(多了一套属性,但并不影响使用)。
//构造函数1
function Person (name) {
    this.name = name
}
Person.prototype.init = function () {
    console.log(' 我是 Person 原型上的方法')
}

//构造函数2
function Stu (age) {
    this.age = age
    //1.借用构造函数继承,得到父类的属性(放在了对象上,并没有继承父类原型上的方法)
    Person.call (this,name)
}
//2.利用原型继承,得到父类的属性(原型上)与方法
Stu.prototype = new Person ('这个字符串没有意义')
Stu.prototype.sayHi = function () {
    console.log('我是Stu 原型上的方法')
}
//创建实例化对象
const s1 = new Stu (18,'张三')
console.log('Stu实例化对象',s1)
console.log(s1.name)
console.log(s1.age)
s1.init()
s1.sayHi()

拷贝继承

利用for ...in 遍历,可以遍历到对象的原型上的方法

//构造函数1
function Person (name) {
    this.name = name
}
Person.prototype.init = function () {
    console.log(' 我是 Person 原型上的方法')
}

//构造函数2
function Stu (age) {
    this.age = age
    /**
    在子类构造函数实例化父类构造函数,得到父类构造函数的实例化对象,然后利用 for in 可以遍历到原型上的属性这个特点,将实例化对象的属性与其原型上的方法一起拷贝子类构造函数的原型中
    */
    const p1 = new Person ('张三')
    for (let key in p1) {
        Stu.prototype[key] = p1[key]
    }
}
Stu.prototype.sayHi = function () {
    console.log('我是Stu 原型上的方法')
}
const s1 = new Stu(18,'张三')
console.log('Stu 实例化对象',s1)
console.log('Stu 原型对象',s1__proto__)

ES6 类的继承

  • 语法要求:
    1. 书写子类的时候: class 子类类名 extends 父类类名 {...}.
    1. 书写 constructor 的时候: 内部需要书写 super('父类需要的参数').
  • 注意:
    1. extends 和 super 必须同时出现才能完成继承.
    1. super 必须出现在 constructor 的第一行.
  • 额外扩展: ES6 类也能继承 ES5 的构造函数;验证方法: 将 Person ES6 类的写法更改为 ES5 的构造函数写法即可.
// 父类
        class Person {
            constructor(name) {
                this.name = name
            }
            init() {
                console.log('我是 Person 原型上的方法')
            }
        }

        // 子类
        class Stu extends Person {
            constructor(age) {
                super('父类需要的参数, 都写在这里边')

                this.age = age
            }
            sayHi() {
                console.log('你好~~~~')
            }
        }

        const s1 = new Stu(18)

        console.log(s1)
        console.log(s1.name)
        s1.init()