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~~~')
}
}
请登录后查看回复内容