本人刚写了一篇原型链的封装继承多态,用家有儿女做的demo。其实我个人感觉封装和多态都容易去理解与实现。关键在于继承,js的才是比较难的,也容易让人混乱,至少我是因为继承头大过\(^o^)/~
js中有很多方法可以实现继承,这篇文章主要对继承的方法进行学习与测试。
这里插一句,个人习惯是展示代码,展示效果,完了去解释发生了什么。因为我感觉对于大部分人来说,光听理论还是不行,最重要的是让他们看到清晰的代码和输出流程,这样才能知道每一步都发生了什么,最后个人开始悟。我大部分文章都是这么写的,希望大家能理解我的思路
//1.原型链继承
//妈妈刘梅的构造函数
function Liumei(){
this.name = '刘梅'
//方法1:定义时添加
this.xuexing = 'o型血'
}
//方法2:定义完毕再添加
// Liumei.prototype.xuexing = 'o型血'
//儿子刘星的构造函数
function Liuxing(){
this.name = '刘星'
}
//刘星的原型对象继承刘梅,核心代码就是这句
Liuxing.prototype = new Liumei()
console.log(Liuxing,'继承后刘星的构造函数')
console.log(Liuxing.prototype,'刘星的原型对象')
let liuxingPerson = new Liuxing()
console.log(liuxingPerson,'刘星本人')
console.log(liuxingPerson.name,'刘星本人名字')
console.log(liuxingPerson.xuexing,'刘星本人血型')
这里最关键的一步是将刘星构造函数的原型对象指向了刘梅的构造函数,这里就实现了继承,主要看那句输出 '刘星的原型对象'。
这里就要说到一个大家比较熟悉的例子了,vue2中的全局事件总线
new Vue({
beforeCreate () {
Vue.prototype.$bus = this //这里就是用的这种继承方法
},
router,
render: h => h(App)
}).$mount('#app')
//2.子构造函数继承父构造函数
//写法1
function Liumei(){
console.log(this,'这里的this在new刘星时,就是刘星实例对象')
this.name = '刘梅'
this.xuexing = 'o型'
}
function Liuxing(){
Liumei.call(this)
//这段代码要写在call方法后面,如果写在call方法前面,名字就会被覆盖掉。如果感觉不合适,可以让Liumei构造函数添加一个name参数,在call方法中加入刘星的名字
this.name = '刘星'
}
// //写法2
// function Liumei(name = '刘梅'){
// this.name = name
// this.xuexing = 'o型'
// }
// function Liuxing(name){
// Liumei.call(this,name)
// }
let liuxingPerson = new Liuxing('刘星')
console.log(liuxingPerson,'刘星本人')
两种写法实现的效果相同,其实这里的代码很简单,很明白就看出哪一句是关键代码。多说一句,一定要很清楚this在各种情况下的指向,这对于你理解和读懂代码很关键
比如在这里,两个构造函数中,this就是实例对象,那么new Liuxing就是刘星本人这个对象,call再将刘梅的this指到刘星的实例对象,所以刘梅构造函数中定义的属性就会保存在刘星实例对象中。
//3.Object.create继承
let LiumeiPerson = {
name:'刘梅',
xuexing:'o型'
}
let liuxingPerson = Object.create(LiumeiPerson)
liuxingPerson.name = '刘星'
console.log(liuxingPerson,'刘星本人')
这个方法属于Object上的一个方法,创建了新的对象,把原有对象的属性添加到这个新对象上,具体可以去MDN上查看
//4.寄生式继承
let LiumeiPerson = {
name:'刘梅',
xuexing:'o型'
}
function ParasitismFun(motherObj){
//记住,箭头函数不能当构造函数
function NewFun (name){
this.name = name
}
NewFun.prototype = motherObj
return NewFun
}
//传递刘梅的对象,将刘梅的对象属性添加一个构造函数的原型的原型上,返回值是那个添加了刘梅属性的构造函数
let Liuxing = ParasitismFun(LiumeiPerson)
console.log(Liuxing,'刘星构造函数')
let liuxingPerson = new Liuxing('刘星')
console.log(liuxingPerson,'刘星本人')
其实就是一个封装的构造函数,将刘梅的属性都添加到刘星构造函数的原型上,这样刘星构造函数在创建实例对象的时候,就能得到刘梅对象上的属性,完成了属性的继承。
//5.class类继承
class Liumei{
constructor(name){
this.name = name
this.xuexing = 'o型'
}
}
class Liuxing extends Liumei{
constructor(name){
super(name)
}
}
let liuxingPerson = new Liuxing('刘星')
console.log(liuxingPerson,'刘星本人')
这个没什么好说的,es6中class的特性,不太熟悉class的人可以去查阮一峰es6,也可以去看我之前的那篇class类的使用,不是很难。
主要就是这5种方法,总而言之,就是让一个对象得到另一个对象身上的属性。
在必要的情况下,也可以将原型链继承和构造函数继承,寄生式继承等配合使用。
框架的源码中很多都会用到继承,一定要好好弄得继承的意义和使用方式,对个人提升很有帮助。