Prototype源码浅析——Object部分(一)

前些天小小的分析了一下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'
};

现在我想把这两个对象的属性合并到一个对象里面
实现方法有两种:
第一,新建一个空对象,然后把两者都拷贝进去(这个在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

转载于:https://www.cnblogs.com/xesam/archive/2011/12/21/2295336.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值