文章背景
2019年本命年已经够坎坷了,没想到2020开局更难,因疫情原因,公司公告说为了可持续发展,1月工资要等疫情稳定后在发放,当时心里就不平衡,本来打算再好好干半年,公司这边操作真是猝不及防,那好吧,第二天我直接提了离职继续在家躺尸,躺了20多天也累了,昨天回了长沙,准备好好复习一下基础知识,然后再是把驾照拿了,再去广州或深圳发展,不想再呆小公司了,太不靠谱了。
简介
javascript中有很多种继承方式,比如组合继承,原型式继承,寄生式继承等,而寄生组合式继承结合了上面继承方式的优点,避免了其缺陷。寄生组合式继承的大致实现逻辑为通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。
代码示例
代码截取自Vue-0.1的源码, 没想到吧!Vue的源码里面包含我今天要复习的知识点。
function ViewModel(){
// .....
}
ViewModel.extend = extend
function extend (options) {
var ParentVM = this
var ExtendedVM = function (opts, asParent) {
if (!asParent) {
opts = inheritOptions(opts, options, true)
}
ParentVM.call(this, opts, true)
}
// ExtendedVM.prototype继承ParentVM.prototype
var proto = ExtendedVM.prototype = Object.create(ParentVM.prototype)
// prototype的constructor指向构造函数
utils.defProtected(proto, 'constructor', ExtendedVM)
return ExtendedVM
}
下面解释一下上面的代码:
继承方法
- ViewModel为超类型构造函数或者可以称为父类构造函数
- ExtendedVM为子类型构造函数
- 在函数内部,创建父类型原型的一个副本, 这行代码
Object.create(ParentVM.prototype)
,为创建的副本添加 constructor 属性,从而弥补因重写原型而失去的默认的 constructor 属性utils.defProtected(proto, 'constructor', ExtendedVM)
,将新创建的对象(即副本)赋值给子类型的原型。
继承属性
在ExtendedVM中调用父类的构造函数ParentVM.call(this, opts, true)
来继承不可共享的属性。
优点
const ChildVue = ViewModel.extend({})
child = new ChildVue() // 这个里面调用ViewModel的构造函数
这个例子的高效率体现在它只调用了一次 ViewModel构造函数,并且因此避免了在 ChildVue.prototype 上面创建不必要的、多余的属性。与此同时,原型链还保持不变。
后话
写到这里差不多已经写完了,下次面试官问你寄生组合式继承可以直接搬Vue的源码跟他聊,这时他看到你还不差的回答,可能会问上面的代码中Object.create的内部是怎么实现的,别急,下面这段代码告诉你Object.create怎么实现:
var _create = function (o) {
var F = function () {}
F.prototype = o
return new F()
}
// 或者也可以这样
_create = function (o){
var t = {}
t.__proto__ = o
return t
}
看到这,面试官可能会问__proto__
与prototype
的区别以及原型链相关的知识了,别担心, 我在掘金上看到了一篇讲原型链特别好的文章(不是我写的,他写的太好了,忍不住推荐),我现在推荐给你。
帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)
再推荐一篇相关的文章给你,也是关于javascript继承的,他们的文章都写的太好了