专业编程基础技术教程

网站首页 > 基础教程 正文

前端设计模式之混入模式(Mixin)

ccvgpt 2024-11-25 10:11:44 基础教程 1 ℃

Mixin是JavaScript中用的最普遍的模式,几乎所有流行类库都会有Mixin的实现。

Mixin是掺合,混合,糅合的意思,即可以就任意一个对象的全部或部分属性拷贝到另一个对象上。

前端设计模式之混入模式(Mixin)

从提供的接口来看,有的是对对象的操作,有的是对类的操作。对类的操作又称为掺元类(Mixin classes)

一、掺合对象 (Mixin object)

先看最简单的mixin实现


function mixin(dest, src) {
	for (var key in src) {
		dest[key] = src[key]
	}
}

使用下

var person = {
	name: 'John',
	age: 29
}
var obj = {}
mixin(obj, person)
console.log(obj) // {name: 'John', age: 29}

可看到,已经将person的所有属性拷贝到obj上了。 有个缺点,如果obj上已经有了name: 'Jack',那么会被person覆盖。因此需要加个判断,如果dest上已经存在了,就不拷贝。


function mixin(dest, src) {
	for (var key in src) {
		if (!dest[key]) {
			dest[key] = src[key]
		}
	}
}
var person = {
	name: 'John',
	age: 29
}
var obj = {
	name: 'Jack'
}
mixin(obj, person)
console.log(obj) // Object { name="Jack", age=29}

当然,你可以提供更强大,灵活的Mixin,比如可以将任意多个对象掺合到目标对象


function mixin(dest /*, Any number of objects */ ) {
	var sources = Array.prototype.slice.call(arguments, 1)
	for (var i = 0; i < sources.length; i++) {
		var src = sources[i]
		for (var key in src) {
			if (!dest[key]) {
				dest[key] = src[key]
			}
		}
	}
}
var person = {
	name: 'John',
	age: 29,
	toString: function() {
		return 'aa'
	}
}
var permission = {
	open: 1
}
var obj = {
	name: 'Jack'
}
mixin(obj, person, permission)
console.log(obj) // Object { name="Jack", age=29, open=1}

以下类库都是对对象的掺合

  • jQuery的$.extend 操作对象,将其它对象的属性方法拷贝到目标对象。
  • RequireJS的私有的mixin 操作对象,将其它对象的属性方法拷贝到目标对象。
  • ExtJS的Ext.apply 也是操作对象,它还提供了一个defaults参数。
  • Underscore.js 的 _.extend,把第二个参数起的所有对象都拷贝到第一个参数
  • Vue中的mixin

二、掺和类(Mixin Classes)

有的翻译过来叫做掺元类,它是一种不需要用到严格的继承就可以复用代码的一种技术。如果多个类想用到某个类的某个方法,可以通过扩充这些类的原型已达到共享该方法。比如先创建一个包含各种通用方法的类,然后让其它类扩充于它。这个包含通用方法的类就叫掺元类。多数时候它不会直接实例化或调用,而是作为其它类的模板用于扩充。

先看最简单的实现


// 工具方法,实现mixin

function augment(destClass, srcClass) {
	var destProto = destClass.prototype
	var srcProto = srcClass.prototype
	for (var method in srcProto) {
		if (!destProto[method]) {
			destProto[method] = srcProto[method]
		}
	}
}
function Person() {} // 具有两个方法的类,用于mixin
Person.prototype.getName = function() {}
Person.prototype.getAge = function() {}
function Student() {} // 没有任何方法的类
augment(Student, Person) // 调用,拷贝
var s1 = new Student()
console.log(s1) // Student { getName=function(), getAge=function()}

工具函数augment接受两个参数,都是函数类型(类),第一个类会从第二个类的原型上继承其方法。即使用Person类扩充了Student类。

我们知道,某些语言如C++/Python允许子类继承多个父类,但在JavaScript中是不允许的,因为一个构造器只有一个原型对象,不过这可以通过多个掺元类的方式实现扩充,这实际是一种变相多继承的实现。和mixin方法类似,修改下augment方法。


function augment(destClass, /*, Any number of classes */ ) {
	var classes = Array.prototype.slice.call(arguments, 1)
	for (var i = 0; i < classes.length; i++) {
		var srcClass = classes[i]
		var srcProto = srcClass.prototype
		var destProto = destClass.prototype
		for (var method in srcProto) {
			if (!destProto[method]) {
				destProto[method] = srcProto[method]
			}
		}
	}
}

这样就实现了多继承。

有时不想继承所有的方法,指向拷贝指定的方法,增加一个参数methods

function augment(destClass, srcClass, methods) {
	var srcProto = srcClass.prototype
	var destProto = destClass.prototype
	for (var i = 0; i < methods.length; i++) {
		var method = methods[i]
		if (!destProto[method]) {
			destProto[method] = srcProto[method]
		}
	}
}
function Person() {}
Person.prototype.getName = function() {}
Person.prototype.setName = function() {}
Person.prototype.getAge = function() {}
Person.prototype.setAge = function() {}
function Student() {}
augment(Student, Person, ['getName', 'setName'])
var s1 = new Student()
console.log(s1) // Student { getName=function(), setName=function()}

Backbone是广泛使用掺元类的库

首先,Backbone库自身就采用Mixin classes方式组织,如Backbone.Events是最底层的掺元类,它的方法(on/off/trigger...)都被Backbone.Model/Backbone.Collection/Backbone.View等继承。代码片段如下


_.extend(Model.prototype, Events, {
...
})
_.extend(Collection.prototype, Events, {
...
})
_.extend(View.prototype, Events, {
...
})

它这里使用_.extend来扩充Model,Collection,View的原型,把Events的方法都拷贝到原型。即Event就是一个掺元类(虽然被实现为一个对象)

其次,我们使用Backbone开发时,你写的模型会用Backbone.Model去扩充,视图会用Backbone.View去扩充。如

var MyModel = Backbone.Model.extend({
instanceProp: xx
}, {
classProp: yy
})
var MyView = Backbone.Model.extend({
instanceProp: xx
}, {
classProp: yy
})

这时,Backbone.Model/Backbone.View等就是掺元类了。当然,你还可以把underscore当做掺元对象,因为Backbone的很多类都继承了_.extend方法,如Backbone.Events/Backbone.Model等。

Tags:

最近发表
标签列表