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