本文首发自「慕课网」,想了解更多IT干货内容,程序员圈内热闻,欢迎关注!
JavaScript 是一种弱类型的语言,且函数是一等公民,因此在代码的组织上非常灵活,有非常多的方法可以实现代码的复用。
“函数是一等公民”的意思,即函数和其他的类型一样,并没有什么特殊。因此在 JavaScript 中,函数可以和其他类型的数据一样,被当成值返回、被当成参数传递、被不同的变量赋值/引用等。
具体到代码复用方面,在 JavaScript 中被使用最广泛的就是类和 Mixin。
类,即 Class,在 Java 等语言中,类是最基本的概念,所有的代码都要基于类来编写。对类一个最直观的理解是“它是对象的蓝本,对象的形状由类的定义来决定”。因此,只要我们将需要复用的代码封装到类中,然后在不同的场合都使用这个类的实例,就可以在各种不同的场合来复用这些相同的代码。
如果我们希望复用一部分代码,但是又希望不同场合的对象形状是不同的,则可能需要使用类的继承:将需要复用的代码放到基类中,将不同的部分放入不同的子类中,然后不同场合视需要使用不同子类的实例,但这些实例仍然可以共享基类的方法和成员,从而实现代码复用。
class Animal{ constructor() { this.head = 1; } eat() { }}class Cat extends Animal{ constructor() { super(); this.legs = 4; } makeSound() { console.log('miao'); }}
在这个例子中,我们定义了一个类Animal,它头(head)的数量为1,并且有eat()方法。接下来有一个Cat类继承自Animal类,它加上了自定义的成员legs和makeSound(),并且从基类Animal中继承了head和eat(),因此它既可以使用自定义的成员,也可以利用基类的成员,这便是继承复用代码的方式。
在 ES6 之前,JavaScript 中是没有正统的“类”的概念的,这也是 JavaScript 饱受误解的一个重要来源:习惯了使用“类”来组织代码的开发者来到了 JavaScript 的世界后,发现他们熟悉的概念和模式都不见了,因此觉得这门语言本身是有非常大的缺陷甚至是“玩具语言”。
但是因为 JavaScript 在设计之初有参考一些 Java 的概念,加上它非常灵活,懂这门语言的开发者仍然可以使用一些方法来模拟类,从而将经典的基于类的概念和模式引入 JavaScript 中。这些方法中使用最广泛的就是“构造函数”加“基于原型的继承”。
上面的例子用构造函数和基于原型的继承,写起来类似这样:
function Animal(){ this.head = 1;}Animal.prototype.eat = function() {};function Cat(){ this.legs = 4;}Cat.prototype = new Animal();Cat.prototype.makeSound = function() { console.log('miao');};
ES6 正式为 JavaScript 加上了“类”的概念,但它在概念和性质上与基于原型的类模拟相差不多,在日常使用的场景下,基本上可以认为它们是相同的。
而有时候为了代码的灵活或者兼容,开发者仍然会使用 ES6 之前的类的写法,Vue 源代码中亦是如此。为了避免无谓的争论,后文中将不区分 ES6 的类和 ES5 基于原型的模拟类,而统称为“类”。
Mixin 在一些中文文档中也被称为“混入”或者“混元”,是另一种实现代码复用的方式。在具体的原理上,类主要通过“继承”来复用代码,而 Mixin 则主要是通过“组合”。
Mixin 的具体做法是:定义一些单独的方法,然后在某一些时机(例如初始化),动态地修改当前使用的对象,将这些方法挂载到对象上去,从而实现不同对象中都可以使用同一个方法。这种行为充分地复用了 JavaScript 语言的动态性,在运行时修改对象,从而改变对象的行为,实现代码复用。
例如在 Vue 的构造函数定义中,就使用了一系列的 mixin 来扩展 Vue 类型的方法,以initMixin()为例:
export function initMixin (Vue: Class<Component>) { Vue.prototype._init = function (options?: Object) { }}
当initMixin()被调用时,就会在传入的Vue变量(Vue 构造函数)的原型上添加_init()方法。
Vue 中大量使用了类似上面示例的 Mixin,从而使得功能代码可以分散在不同的代码文件中,从而保持代码结构简单。
继承和 Mixin 都能很好地达到代码复用的目的,但它们也有各自的优缺点。
当继承使用过多时,容易出现一些问题,例如继承层数过多、大量覆盖父类实现等,使用不当时会导致代码难以维护。关于这一点,可以参考更多的资料,例如被广泛流传的“组合优于继承”的说法,在此不再赘述。
Mixin 也有一些缺点,例如代码组织过于灵活,很容易导致代码间的互相引用混乱,从而影响可维护性。
因此在实际使用中,选择使用继承还是使用 Mixin 来组织代码并不是一个有明确答案的问题,需要根据实际情况来进行取舍。Vue 中大量使用了 Mixin 来组织代码。
了解类和 Mixin 有助于我们从宏观上去构架代码,从而使文件组织更合理,代码结构更可读。而具体到 Vue 源码上,则能帮助我们拨开 Vue 源码结构上的迷雾,使我们在阅读 Vue 源码时不被与类和 Mixin 有关的代码干扰,从而更好地理解 Vue 的代码组织形式和各个部分的实现原理。
欢迎关注「慕课网」,发现更多IT圈优质内容,分享干货知识,帮助你成为更好的程序员!