# extends **Repository Path**: ifercarly/extends ## Basic Information - **Project Name**: extends - **Description**: 说完继承的前世今生 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 6 - **Forks**: 0 - **Created**: 2021-11-15 - **Last Updated**: 2022-06-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 01. Call 式继承 又叫借用构造函数继承。 ```js 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 式继承的问题 只能继承属性。 ```js const s = new Star('舒总', 18) s.say() // TypeError: s.say is not a function ``` ## 03. 原型继承 ```js 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. 原型继承的问题 ```js const s = new Star('舒总', 18) console.log(s) // 污染了子类的原型,多挂载了没有意义的父类属性 ``` ## 05. 组合继承 组合继承 = Call 式继承(继承属性) + 原型继承(继承方法),其实上面的 01 和 03 结合起来就是组合继承。 ## 06. 寄生继承 所谓寄生继承,就是把**父类的原型**“寄生”到**新函数的原型**上,再把新函数的实例赋值给子类的原型。 ```js 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. 优化寄生继承写法 ```js 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`。 ```js 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 的时候能写的简洁一些。 ```js 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`。 ```js 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 个参数 ```js value // 默认 undefined writable // 默认 false,值是否可以被修改 enumerable // 默认 false,值是否可枚举 configurable // 默认 false,值是否可以被删除 ``` ```js get set enumerable configurable ``` ```js 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 中的实例属性、原型属性、静态属性 ```js 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~~~') } } ```