1 Star 6 Fork 0

ifer / extends

Create your Gitee Account
Explore and code with more than 8 million developers,Free private repositories !:)
Sign up
This repository doesn't specify license. Please pay attention to the specific project description and its upstream code dependency when using it.
Clone or Download
contribute
Sync branch
Cancel
Notice: Creating folder will generate an empty file .keep, because not support in Git
Loading...
README.md

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) // 污染了子类的原型,多挂载了没有意义的父类属性

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)

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~~~')
    }
}

About

说完继承的前世今生 expand collapse
JavaScript
Cancel

Releases

No release

Contributors

All

Activities

Load More
can not load any more
1
https://gitee.com/ifercarly/extends.git
git@gitee.com:ifercarly/extends.git
ifercarly
extends
extends
master

Search