前些天小小的分析了一下Prototype的Function部分,今天開始Prototype的Object這一部分
開始需要說明的一點是,Object這部分與先前的Function那一部分有點不一樣,這次Object是直接擴充在Object上面的,而非Object.prototype,兩者是有本質區別的,也就是本次Obeject的擴充相當於添加Object的一個靜態變數(方法);
extend(Object, {})
Object.extend(Function.prototype,{})
Object的方法比較多,有些是很基礎的(比如類型判斷),所以也就不想全部都分析了,只揀幾個我覺得比較重要或者難以理解的說一下:
extend(Object, {
extend: extend,
inspect: inspect,
toJSON: NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON,
toQueryString: toQueryString,
toHTML: toHTML,
keys: Object.keys || keys,
values: values,
clone: clone,
isElement: isElement,
isArray: isArray,
isHash: isHash,
isFunction: isFunction,
isString: isString,
isNumber: isNumber,
isDate: isDate,
isUndefined: isUndefined
});
第一個比較重要的是extend方法,這個方法很多書上面都有,而且基本都差不多,就是複製屬性的一個方法。
後面(包括Function.prototype)的屬性添加大都是用這個方法來進行的。
一個執行個體:
現在有兩個對象:
var obj_1 = {
name : 'xesam'
};
var obj_2 = {
age : '24',
name: 'xesam_1'
};
現在我想把這兩個對象的屬性合并到一個對象裡面
實現方法有兩種:
第一,建立一個Null 物件,然後把兩者都拷貝進去(這個在Prototype裡面對應的是clone方法,不過clone是需要一個對象就行)
第二,把obj_2的屬性拷貝到obj_1裡面去
其實上面兩種方法其實沒有必要分得那麼清楚,因為對應的方法都是一樣的。
如果不考慮深層拷貝和重複屬性,那麼實現是很簡單的,比如:
(function(){
return;
var obj_1 = {
name : 'xesam'
};
var obj_2 = {
age : '24',
name: 'xesam_1'
};
function extend(destination, source) {
for (var property in source){
destination[property] = source[property];
}
return destination;
}
extend(obj_1,obj_2);
console.log(obj_1);//name變成xesam_1
})();
如果你去看一下Prototype的源碼,你會發現這貨還真是這麼做的,相比jquery的實現,簡直就是弱爆了。
拋開Prototype的應用環境與這麼做的目的不說,這種實現如果被用在平常的使用中,會碰到的問題就是,
第一、如果有兩個相同的屬性怎麼處理?
第二、in操作在不同瀏覽器的實現中返回的結果可能並不一致
按照上面的實現,source中的屬性會覆蓋destination中的同名屬性。如果這並非我們本意,那麼我們稍微改一下代碼就可以:
(function(){
return;
var obj_1 = {
name : 'xesam'
};
var obj_2 = {
age : '24',
name: 'xesam_1'
};
function extend(destination, source) {
for (var property in source){
if(destination.hasOwnProperty[property]){
destination[property] = source[property];
}
}
return destination;
}
extend(obj_1,obj_2);
console.log(obj_1);//name還是xesam
})();
因此我們可以再添加一個參數overwrite來實現更靈活的配置,順便複習一下Function部分的wrap方法
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
</head>
<body>
<script type="text/javascript">
Function.prototype.bind = function(context) {
var __method = this, args = Array.prototype.slice.call(arguments, 1);
return function() {
var b = args.concat(Array.prototype.slice.call(arguments, 0));
return __method.apply(context, b);
}
}
Function.prototype.wrap =function(wrapper){
var _this = this;
return function(){
var params = [_this.bind(this)].concat(Array.prototype.slice.call(arguments, 0));
wrapper.apply(this, params);
}
}
function extend(destination, source,overwrite) {
for (var property in source){
if(overwrite){
destination[property] = source[property];
}else if(destination.hasOwnProperty[property]){
destination[property] = source[property];
}
}
return destination;
}
Object.prototype.extend = extend.wrap(function(process,source,overwrite){
if(overwrite || false){
process(this,source,true);
}else{
process(this,source,false);
}
});
(function(){
var obj_1 = {
name : 'xesam'
};
var obj_2 = {
age : '24',
name: 'xesam_1'
};
obj_1.extend(obj_2);//沒有重寫obj_1的name
console.log(obj_1);
})();
(function(){
var obj_1 = {
name : 'xesam'
};
var obj_2 = {
age : '24',
name: 'xesam_1'
};
obj_1.extend(obj_2,true);//重寫了obj_1的name
console.log(obj_1);
})();
</script>
</body>
</html>
【說明:這樣的調用方法根本沒有必要,純粹是為了熟悉一下wrap方法而已】
這裡我們調用obj_1.extend(obj_2,true);的時候,會一併拷貝我們新添加的extend方法。
因為extend是我們自訂的,所以都會被in操作枚舉出來,下面的第二個問題就和這個自訂的屬性有關。
另外一個問題就是in操作在某些IE6下的實現問題。
假設有下列情況,如果我們在某一source對象中重寫了類似toString()這類原型中不可枚舉的屬性,那麼在正常情況下,用in操作符是可以擷取重寫後的toString的(就像上例我們添加的extend一樣),但是IE6在這種情況下是繼續屏蔽toString的,所以取不到這個值
看下面的例子:
(function(){
return;
var obj_1 = {
name : 'xesam'
};
var obj_2 = {
age : '24',
toString:function(){return ('my toString');}
};
function extend(destination, source) {
for (var property in source){
destination[property] = source[property];
}
return destination;
}
extend(obj_1,obj_2);
alert(obj_1.toString());
})();
正常情況下,因該是彈出對話方塊“my toString”,但是在IE6由於obj_1並沒有獲得這個重寫,還是調用了原來Object本身的toString方法,因此彈出對話方塊為“object Object”。
因此如果想更通用的話,還得事先檢測一下這個相容性的問題,一個參考代碼:
(function(){
var DontEnum = (function (){ //(1)先檢測是否有屏蔽自訂屬性的問題
var o = {toString:''}
for(var i in o){
return true;
}
return false;
})();
var DontEnumArray = ['toString','valueOf'];//(2)這個數組還有補充的地方
var obj_1 = {
name : 'xesam'
};
var obj_2 = {
age : '24',
toString:function(){return ('my toString');}
};
function extend(destination, source) {
if(!DontEnum){
for(var j = 0,len = DontEnumArray.length; j < len; j++){
if(source.hasOwnProperty(DontEnumArray[j])){
destination[DontEnumArray[j]] = source[DontEnumArray[j]];
}
}
}
for (var property in source){
destination[property] = source[property];
}
return destination;
}
extend(obj_1,obj_2);
alert(obj_1.toString());
})();
【說明(1)(2):這段代碼並不完善,因為還得確定IE6或者其他的瀏覽器到底屏蔽了哪些屬性,需要一一測試】
Object的靜態方法先說這個,還有其他的事,後面的明天再繼續。。。
轉載請註明來自小西山子【http://www.cnblogs.com/xesam/】
本文地址:http://www.cnblogs.com/xesam/archive/2011/12/21/2295336.html