结合vue源码复习javascript寄生组合式继承

1,934 阅读3分钟

文章背景

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
}

下面解释一下上面的代码:

继承方法

  1. ViewModel为超类型构造函数或者可以称为父类构造函数
  2. ExtendedVM为子类型构造函数
  3. 在函数内部,创建父类型原型的一个副本, 这行代码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继承的,他们的文章都写的太好了

寄生组合式继承