天天看點

dojo.mixin、dojo.extend、dojo.delegate解析dojo.mixin dojo.extenddojo.delegate

dojo.mixin

dojo.mixin函數簽名為:function(obj, props){;其中obj為目标對象,props為源對象,可以傳入多個,該方法用于将其它對象中的屬性(也包括函數屬性)混入至目标對象,然後傳回目标對象,如果混入源對象與目标對象同時存在某個屬性時,則源對象屬性會覆寫目标對象屬性,但是并不會修改目标對象的prototype。

<script type="text/javascript">
	var a = {
		name : "zhangsan",
		address : "地球"
	};
	var b = {
		age : 18
	};
	var c = {
		address : "中國"	
	};
	
	//将b、c對象中的屬性混入目标對象a
	var mixed = dojo.mixin(a, b, c);
	//輸出為 Object { name="zhangsan",  address="中國",  age=18}
	//可以看到已包含三個屬性,并且目标對象a的address屬性被源對象c的相對屬性覆寫了
	console.info(mixed);
	//結果為true,因為混入後目标對象被傳回了,隻是往目标對象中添加了屬性,但是目标對象位址并沒有發生改變
	console.info(mixed==a);
</script>
           

下面我們看一下dojo中的源碼:

dojo.mixin = function(/*Object*/obj, /*Object...*/props){
	if(!obj){ obj = {}; }	//如果obj目标對象不存在,則賦予一個空對象,防止異常
	for(var i=1, l=arguments.length; i<l; i++){	//周遊混入源對象,注意索引從1開始,因為需要排除obj目标對象
		d._mixin(obj, arguments[i]);//調用真正實作混入的_mixin函數
	}
	//傳回原來的目标對象
	return obj; // Object
}

var extraNames, extraLen, empty = {};
//如果toString(覆寫從Object繼承的toString)不能在for..in循環中周遊到,則extraNames為undefined,如果能則指派為空清單
for(var i in {toString: 1}){ extraNames = []; break; }
//如果extraNames存在則dojo._extraNames會等于extraNames,即是一個空清單
//如果extraNames為undefined,則dojo._extraNames為["hasOwnProperty", "valueOf", "isPrototypeOf", "propertyIsEnumerable", "toLocaleString", "toString", "constructor"]
dojo._extraNames = extraNames = extraNames || ["hasOwnProperty", "valueOf", "isPrototypeOf",
	"propertyIsEnumerable", "toLocaleString", "toString", "constructor"];
extraLen = extraNames.length;//擷取長度

dojo._mixin = function(/*Object*/ target, /*Object*/ source){
	var name, s, i;
	
	//周遊源對象中的屬性
	for(name in source){
		//在混入的時候需要排除從Object繼承而來的屬性,除非源對象重新定義了從Object繼承而來的屬性
		s = source[name];//取出屬性值
		//成立條件有:
		//1. 目标對象中不存在源對象中的某個屬性
		//2. 目标對象與源對象屬性值不相等并且 (該屬性不是從Object繼承而來或者源對象重新定義了從Object繼承而來的屬性)
		if(!(name in target) || (target[name] !== s && (!(name in empty) || empty[name] !== s))){
			//将源對象屬性值賦給目标對象
			target[name] = s;
		}
	}
	
	// IE doesn't recognize some custom functions in for..in	有些版本的IE對于重新定義從Object繼承來而的屬性不能識别,是以需要重新判斷一次
	if(extraLen && source){
		for(i = 0; i < extraLen; ++i){
			name = extraNames[i];
			s = source[name];
			if(!(name in target) || (target[name] !== s && (!(name in empty) || empty[name] !== s))){
				target[name] = s;
			}
		}
	}
	//傳回目标對象
	return target; // Object
}
           

dojo.extend

dojo.extend函數簽名為:function( constructor, props); 接受一個構造函數和多個源對象。如果第一個參數不是一個構造函數,确切點說是不包含一個prototype屬性。該函數用于将多個源對象中的屬性混入到一個構造函數的prototype屬性當中,最傳回這個構造函數。該函數就是基于dojo.mixin實作的,一看源碼便知:

dojo.extend = function(/*Object*/ constructor, /*Object...*/ props){
		// summary:
		//		Adds all properties and methods of props to constructor's
		//		prototype, making them available to all instances created with
		//		constructor.
	for(var i=1, l=arguments.length; i<l; i++){
		d._mixin(constructor.prototype, arguments[i]);
	}
	return constructor; // Object
};
           

因為混入的目标對象是構造函數的prototype屬性,是以通過該構造函數建立(new)出來的對象會包含所有源對象中的屬性。

由上可知,mixin适用于一次性混入,而extend适用于永久性混入(因為改為了prototype)

dojo.delegate

該函數調用後傳回一個新對象,并且會對這個新對象的[[prototype]]屬性混入新的屬性,源碼如下:

dojo.delegate = dojo._delegate = (function(){
	//臨時構造函數
	function TMP(){}
	//傳回一個匿名函數,該函數其實就是dojo.delegate函數
	return function(obj, props){
		TMP.prototype = obj;
		var tmp = new TMP();
		//雖然TMP.prototype重新置為null,但是tmp對象已經建立出來了,其[[prototype]]屬性已經指向了obj,
		//是以通過tmp對象可以通路到obj對象中的屬性
		TMP.prototype = null;
		//如果props存在,則将props混入tmp對象中,是以tmp對象中就有了props對象的所有屬性
		if(props){
			d._mixin(tmp, props);
		}
		return tmp; // 傳回tmp對象
	};
})();//立即執行
           

由上可知,dojo.delegate與dojo_delegate是一樣的,指向了同一個函數對象。js中,每一個對象建立出來都會有一個[[prototype]]屬性,也就是建立這個對象的構造函數的prototype屬性,在FireFox中[[prototype]]屬性可以通過__proto__來通路,請看下面的例子:

function Foo() {
	this.name = "Foo";
}
var foo = new Foo();
var de = dojo.delegate(foo, {address : "中國"});
console.info(de);	//Foo {address="中國", name="Foo"}
//因為在delegate函數中執行了TMP.prototype = obj;
console.info(de.__proto__==foo);	//true
           

PS:dojo版本為dojo-release-1.6.3