摻合模式(Mixin)


Mixin是JavaScript中用的最普遍的模式,幾乎所有流行類庫都會有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,把第二個參數起的所有對象都拷貝到第一個參數

 

二、摻和類(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等。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM