只遍历出JScript对象的expando属性
我们知道JScript的对象(不只是Object)可以像html元素对象一样,添加任意的自定义属性值。也就是说JScript的对象,天生都是Key/Value map。并且这种map结构由于使用Native的代码实现,检索效率非常之高,我曾在这篇文章里讨论过。不过在遍历这样的map时,expando导入属性和prototype导入属性存在了混淆。
由于JScript的prototype特性对对象的扩充非常的方便,所以我们在制作一些JScript类库的时候,一般都会使用prototype特性为对象添加方法,比如我们对Object进行如下prototype扩充:
Object.prototype.Clone = function() {};
Object.prototype.Call = function() {};
Object.prototype.OtherMethod = function(){};
这个时候如果再使用Object作为map结构来使用,我们就会遇到遍历这个map的错误,看下面的代码:
var objMap = {};
objMap['abc'] = '1.abc';
objMap['def'] = '2.def';
objMap['ghi'] = '3.ghi';
objMap['jkl'] = '4.jkl';
遍历这个集合:
function DisplayMap(map)
{
var values = [];
for ( var key in map )
{
values.push(map[key]);
}
return values;
}
Display(objMap);
我们发现,在values里的值居然是:function(){},function() {},function() {},1.abc,2.def,3.ghi,4.jkl。真是郁闷
! 其实这就是 for in 语句的效果,JScript也就是这么设计的,我们没有办法改变。那么我门能不能只取出objMap中expando进去的属性呢?
由于prototype属性的优先级很高,在对象实例生成的时候就expand到对象实例中去了。所以我们建立的任何一个对象,都会包含相同的prototype属性。这样一来就好办了,我们把objMap中的prototype属性找出来过滤掉就行了呗。参考代码如下:
function GetExpandoValues(map)
{
var values = [];
var obj = new map.constructor();
for ( var key in map )
{
if ( obj[key] !== map[key] )
{
values.push(map[key]);
}
}
return values;
}
GetExpandoValues(objMap);
获得结果为:1.abc,2.def,3.ghi,4.jkl
。
由于JScript的prototype特性对对象的扩充非常的方便,所以我们在制作一些JScript类库的时候,一般都会使用prototype特性为对象添加方法,比如我们对Object进行如下prototype扩充:
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
这个时候如果再使用Object作为map结构来使用,我们就会遇到遍历这个map的错误,看下面的代码:
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
遍历这个集合:
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
我们发现,在values里的值居然是:function(){},function() {},function() {},1.abc,2.def,3.ghi,4.jkl。真是郁闷
![](https://birdshome.cnblogs.com/Emoticons/angry_smile.gif)
由于prototype属性的优先级很高,在对象实例生成的时候就expand到对象实例中去了。所以我们建立的任何一个对象,都会包含相同的prototype属性。这样一来就好办了,我们把objMap中的prototype属性找出来过滤掉就行了呗。参考代码如下:
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://birdshome.cnblogs.com/Images/OutliningIndicators/None.gif)
获得结果为:1.abc,2.def,3.ghi,4.jkl
![](https://birdshome.cnblogs.com/Emoticons/teeth_smile.gif)
posted on 2005-06-24 18:02 birdshome 阅读(2565) 评论(15) 编辑 收藏 引用 收藏至365Key 所属分类: Jscript&Dhtml开发
评论
# re: 只遍历出JScript对象的expando属性 回复
昨天还在为这个问题郁闷呢,今天你就找到解决方法了。非常感谢!我一直以为for in只是遍历对象中的属性,因为JScript中的默认方法从来没有遍历出来过,比如toString等,然而prototype的方法却会遍历出来,还好有了这个方法可以过滤掉。
# re: 只遍历出JScript对象的expando属性 回复
不错!不过也由此告诫我们,for in别滥用,否则,如果我是先写的map,之后有一天又为object做扩展就麻烦了~~# re: 只遍历出JScript对象的expando属性 回复
程序有问题。第三行的 objMap 应为 map。
另外这样子之可以得到属性的值,不能得到属性的名字了。我修改了一下,并且prototype到了Object上:
Object.prototype.getExpandoValues = function() {
var values = new Array();
var obj = new this.constructor();
for(var i in this) {
if(obj[i]!=this[i]) {
values.push({"name":i, "value":this[i]});
}
}
return values;
}
效率可能不够高,但是这样就可以使用属性的名字了。
# re: 只遍历出JScript对象的expando属性 回复
@宇义
谢谢提醒,已修正:)
你的这个改进因该说还是不错的,效率上也不会有太大的差别。不过这里有个问题,我们在使用map的时候,如果知道了key,就更本不用遍历了,直接map[key]就能用了,遍历一般都是不知道key的时候使用。而且你这个name:value的对象,要使用也不是很方便,继续用我上面的objMap来作为例子。如果我知道了key为"abc",那么我直接:
但是如果使用你的方法,我知道了key为"abc",我需要这样来做:
是不是反而多此一举呢?当然了我不清楚你的context,也就望文生义了。
# re: 只遍历出JScript对象的expando属性 回复
只有在for in的时候才需要这个方法,有了那种方法也是可以直接var value = objMap[key]的啊。昨晚又修改了一下,回到原点:for in就是为了不遍历到prototype的成员,那么只要用一个变量存储expando属性的“名字”就可以了。属性的值完全可以通过取得的名字直接取得,不需要再存在数组中了。而把名字存储在数组中是因为没有一个有效的办法避过prototype的成员。
Object.prototype.getExpandoNames = function() {
var values = new Array();
var obj = new this.constructor();
for(var i in this) {
if(obj[i]!=this[i]) {
values.push(i);
}
}
return values;
}
使用:
// 假设已有一个对象objMap
var e = objMap.getExpandoNames();
for(var i=0;i<e.length;i++) { // 通过成员名字遍历对象
document.write("name:"+e[i]+", value:"+objMap[e[i]]); // 取值使用 objMap[e[i]]
}
这样在修改以前的for in语句的时候也简单,添加一个变量存储属性名字,改一下for()中的内容,再把循环中的以前的变量(比如i)全部改成e[i]这种形式就可以了。
# re: 只遍历出JScript对象的expando属性 回复
@宇义嗯,不错。这样更加像.NET里的hashtable了,我们除了可以foreach in hashtable外,还可以取hashtable.Keys。这里这个Keys就等同于你取出的Names。
# re: 只遍历出JScript对象的expando属性 回复
老兄,能不能把那个日历改一下,我看着别扭,中国人应该用中国人的习惯,星期日应该是周末,放在最后就顺眼了!!# re: 只遍历出JScript对象的expando属性 回复
@零点这个blog网站的日历默认就是这个样子,我改不了:<
# re: 只遍历出JScript对象的expando属性 回复
for(in)的效率也是很差的# re: 只遍历出JScript对象的expando属性 回复
@ShiningRay没有别的东西可以代替,效率差也只有忍了。当然如果要处理很大很大的内容,使用一些空间换时间的方法,是可以有专门的优化方法的。我们用for in处理general的情况应该还是没有什么问题的。
# re: 只遍历出JScript对象的expando属性 回复
这篇文章真不错,我以前都有点忽略别人还可能添加方法这种情况,看来我是有点个人主义[特指个人开发,俗称:单干]了,以后我会注意一下这个问题了# re: 只遍历出JScript对象的expando属性 回复
什么叫expando?原本以为是开发人员自己添加的对象属性。但昨天这个例子让我反思
注意以下的unselectable属性,它是dhtml可以解析的,但expando=false时,这个属性将失效。显然dhtml也有自己的expando属性。这样概念便模糊了。请大家澄清一下。
<HTML>
<HEAD>
<SCRIPT>
//Set the expando property to false.
document.expando = false;
</SCRIPT>
</HEAD>
<BODY>
<DIV>
<SPAN id="oSpan" unselectable="on">
This text should be selectable.</SPAN>
</DIV>
</BODY>
</HTML>
# re: 只遍历出JScript对象的expando属性 回复
@eagle ov seaRemarks for Unselectable Attribute:
# re: 只遍历出JScript对象的expando属性 回复
有一些地方还是不明白,比如我在这里定义的一个类(另外一篇的singleton类),我想只罗列出它的属性,function Singleton(){
if(this.constructor.instance){
return this.constructor.instance;
}else{
this.constructor.instance = this;
}
this.value = parseInt(Math.random()*100000);
}
Singleton.prototype.getValue = function(){
return this.value;
}
Singleton.prototype.setValue = function(value){
this.value = value;
}
Singleton.prototype.toString = function(){
return "Singleton Class";
}
采用上面最初的function getExpandoValues(map){...}方法还是会把我所定义的getValue和setValue打印出来,我看了你的这么多篇关于prototype的文章也没明白到底是个什么东西-_-,但是为什么不能做这样的修改呢?
function getExpandoValues(map){
var values = [];
var obj = new map.constructor();
for ( var key in map ){
if(typeof(map[key])!="function"){
values.push(map[key]);
}
}
return values;
}
只有不是function类型的才加到数组里,这样显示的结果就只有我想要的value一个,
我加你msn和qq都没反应。。。。
msn: jeremy_z722751@hotmail.com
# re: 只遍历出JScript对象的expando属性 回复
@long_biti我这个方法的目的是取出所有的expando属性,而你的那个setValue和getValue虽然是function类型,但是这两个方法对于其所属对象来说,也是expando属性。
你要滤掉function只是一个特殊需求,完全可以像你那样来修改,不过修改由就不是获得的所有expando了