js继承方式-技术鸭论坛-前端交流-技术鸭(jishuya.cn)

js继承方式

01. Call 式继承

又叫借用构造函数继承。

function Person(name, age) {
    this.name = name
    this.age = age
}
Person.prototype.say = function () {
    console.log('Hello World')
}

function Star(name, age) {
    Person.call(this, name, age) // Person.apply(this, [name, age])
}

const s = new Star('舒总', 18)
console.log(s.name, s.age)

02. Call 式继承的问题

只能继承属性。

const s = new Star('舒总', 18)
s.say() // TypeError: s.say is not a function
 

03. 原型继承

function Person(name, age) {
    this.name = name
    this.age = age
}
Person.prototype.say = function () {
    console.log('Hello World')
}

function Star(name, age) {
    Person.call(this, name, age)
}

Star.prototype = new Person()
Star.prototype.constructor = Star

const s = new Star('舒总', 18)
s.say() // 'Hello World'

 

04. 原型继承的问题

const s = new Star('舒总', 18)
console.log(s) // 污染了子类的原型,多挂载了没有意义的父类属性

c29807306a3a

 

 

05. 组合继承

组合继承 = Call 式继承(继承属性) + 原型继承(继承方法),其实上面的 01 和 03 结合起来就是组合继承。

06. 寄生继承

所谓寄生继承,就是把父类的原型“寄生”到新函数的原型上,再把新函数的实例赋值给子类的原型。

function Person(name, age) {
    this.name = name
    this.age = age
}
Person.prototype.say = function () {
    console.log('Hello World')
}

function Star(name, age) {
    Person.call(this, name, age)
}

// Step1
function Temp() {}
// Step2
Temp.prototype = Person.prototype
// Step3
Star.prototype = new Temp() // 得到的恰恰只有父类原型的方法,不会再往子类原型上挂载多余的属性啦
Star.prototype.constructor = Star

const s = new Star('舒总', 18)
console.log(s)

6538b610d7ab

 

07. 寄生组合继承

其实上面的代码就是寄生组合继承,寄生组合继承 = Call 式继承(继承属性) + 寄生继承(继承方法)。

08. 优化寄生继承写法

function Person(name, age) {
    this.name = name
    this.age = age
}
Person.prototype.say = function () {
    console.log('Hello World')
}

function Star(name, age) {
    Person.call(this, name, age)
}

// 封装成了函数
function create(parentPrototype) {
    function Temp() {}
    Temp.prototype = parentPrototype
    return new Temp()
}

Star.prototype = create(Person.prototype)
Star.prototype.constructor = Star

const s = new Star('舒总', 18)
console.log(s)

 

 

09. 继续优化寄生继承写法

优化目的:省略掉这一行代码 Star.prototype.constructor = Star

function Person(name, age) {
    this.name = name
    this.age = age
}
Person.prototype.say = function () {
    console.log('Hello World')
}

function Star(name, age) {
    Person.call(this, name, age)
}

function create(parentPrototype, Child) {
    function Temp() {
        // this 就是实例,而 #1 处把实例赋值给了 Star.prototype,所以下面代码变成了 Star.prototype.constructor = Child,所以 #2 处的代码可以注释掉啦
        this.constructor = Child
    }
    Temp.prototype = parentPrototype
    return new Temp()
}

Star.prototype = create(Person.prototype, Star) // #1
// Star.prototype.constructor = Star // #2

const s = new Star('舒总', 18)
console.log(s)

 

 

10. 继续优化寄生继承写法

优化目的:调用 create 的时候能写的简洁一些。

function Person(name, age) {
    this.name = name
    this.age = age
}
Person.prototype.say = function () {
    console.log('Hello World')
}

function Star(name, age) {
    Person.call(this, name, age)
}

function create(Parent, Child) {
    function Temp() {
        this.constructor = Child
    }
    Temp.prototype = Parent.prototype // #1
    return new Temp()
}

// 函数封装的目的:是为了让【使用者】更方便,所以这里直接传递 Person 就好啦,不用写那么长了,#1 处的代码要改成 Parent.prototype
Star.prototype = create(Person, Star)

const s = new Star('舒总', 18)
console.log(s)

 

 

11. Object.create()

Object.create 实现继承相比较上面的代码:要多写一行代码 Star.prototype.constructor = Star

function Person(name, age) {
    this.name = name
    this.age = age
}
Person.prototype.say = function () {
    console.log('Hello World')
}

function Star(name, age) {
    Person.call(this, name, age)
}

// 模拟 Object.create() 函数
/* function create(parentPrototype) {
    function Temp() {}
    Temp.prototype = parentPrototype
    return new Temp()
} */

Star.prototype = Object.create(Person.prototype) // Star.prototype.__proto__ = Person.prototype
// 注意:使用 Object.create() 确实可以继承父类原型上的方法,但是会导致 Star.prototype.constructor 也指向了 Person,这并不是我们期望的,所以下面的代码不能省略!
Star.prototype.constructor = Star

const s = new Star('舒总', 18)
s.say()

 

 

12. 了解 Object.create() 的第 2 个参数

value // 默认 undefined
writable // 默认 false,值是否可以被修改
enumerable // 默认 false,值是否可枚举
configurable // 默认 false,值是否可以被删除
get
set
enumerable
configurable
function Person(name, age) {
    this.name = name
    this.age = age
}
Person.prototype.say = function () {
    console.log('Hello World')
}

function Star(name, age) {
    Person.call(this, name, age)
    this.address = '日本'
}

// 注意:第二个配置项会作用于 Star.prototype,而不是 Person.prototype
Star.prototype = Object.create(Person.prototype, {
    address: {
        value: '河南',
    },
})
Star.prototype.constructor = Star

const s = new Star('舒总', 18)
s.address = 'xxx' // 由于 address 的 writable 默认是 false,所以修改不了,即便在 #1 处写也不行,因为 #1 处本质上也是通过实例修改了 address,和这一行代码同样的道理
console.log(s.address) // '河南'

 

 
 
 

13. 关于 class 中的实例属性、原型属性、静态属性

 

class Person {
    constructor(name, age) {
        // constructor 中写的 this.xxx,都是挂载到实例上的
        this.name = name
        this.age = age

        // this.play = () => {} // #1
    }
    // 也是【实例属性】,思考和写在 constructor 中的区别是什么呢?
    address = '河南'

    // 【是实例方法】!!!等价于 #1 处的代码
    play = () => {}

    // 【原型方法】
    say() {
        console.log('Hello World')
    }

    // 【静态属性】
    static version() {
        return 'vue3.0.0'
    }
    // 【静态方法】
    static show = () => {
        console.log('show~~~~')
    }
    // 【静态方法】
    static test() {
        console.log('test~~~')
    }
}

 

 
请登录后发表评论

    请登录后查看回复内容