漫漫技术人生路

C#

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

只遍历出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

    

posted on 2005-06-24 18:02 birdshome 阅读(2565) 评论(15)  编辑 收藏 引用 收藏至365Key 所属分类: Jscript&Dhtml开发

评论

# re: 只遍历出JScript对象的expando属性  回复   

昨天还在为这个问题郁闷呢,今天你就找到解决方法了。非常感谢!

我一直以为for in只是遍历对象中的属性,因为JScript中的默认方法从来没有遍历出来过,比如toString等,然而prototype的方法却会遍历出来,还好有了这个方法可以过滤掉。
2005-06-24 18:58 | 宇义

# re: 只遍历出JScript对象的expando属性  回复   

不错!不过也由此告诫我们,for in别滥用,否则,如果我是先写的map,之后有一天又为object做扩展就麻烦了~~
2005-06-25 10:14 | Teddy

# 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;
}

效率可能不够高,但是这样就可以使用属性的名字了。
2005-06-26 19:36 | 宇义

# re: 只遍历出JScript对象的expando属性  回复   

@宇义
谢谢提醒,已修正:)
    你的这个改进因该说还是不错的,效率上也不会有太大的差别。不过这里有个问题,我们在使用map的时候,如果知道了key,就更本不用遍历了,直接map[key]就能用了,遍历一般都是不知道key的时候使用。而且你这个name:value的对象,要使用也不是很方便,继续用我上面的objMap来作为例子。如果我知道了key为"abc",那么我直接:

var key = "abc";
var value = objMap[key];

但是如果使用你的方法,我知道了key为"abc",我需要这样来做:

var key = "abc";
var objects = objMap.getExpandoValues();
var value = null;
for ( var i = 0 ; i < objects.length ; ++i )
{
    
if ( objects[i].name == key )
    {
        value 
= objects[i].value;
    }
}

是不是反而多此一举呢?当然了我不清楚你的context,也就望文生义了。

2005-06-26 23:02 | birdshome

# 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]这种形式就可以了。
2005-06-27 10:19 | 宇义

# re: 只遍历出JScript对象的expando属性  回复   

@宇义
嗯,不错。这样更加像.NET里的hashtable了,我们除了可以foreach in hashtable外,还可以取hashtable.Keys。这里这个Keys就等同于你取出的Names。
2005-06-27 12:54 | birdshome

# re: 只遍历出JScript对象的expando属性  回复   

老兄,能不能把那个日历改一下,我看着别扭,中国人应该用中国人的习惯,星期日应该是周末,放在最后就顺眼了!!
2005-07-07 18:19 | 零点

# re: 只遍历出JScript对象的expando属性  回复   

@零点
这个blog网站的日历默认就是这个样子,我改不了:<
2005-07-19 19:36 | birdshome

# re: 只遍历出JScript对象的expando属性  回复   

for(in)的效率也是很差的
2005-08-29 23:07 | ShiningRay

# re: 只遍历出JScript对象的expando属性  回复   

@ShiningRay
没有别的东西可以代替,效率差也只有忍了。当然如果要处理很大很大的内容,使用一些空间换时间的方法,是可以有专门的优化方法的。我们用for in处理general的情况应该还是没有什么问题的。
2005-09-26 11:23 | birdshome

# re: 只遍历出JScript对象的expando属性  回复   

这篇文章真不错,我以前都有点忽略别人还可能添加方法这种情况,看来我是有点个人主义[特指个人开发,俗称:单干]了,以后我会注意一下这个问题了
2005-12-25 17:48 | 黄宗银

# 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>
2006-06-06 11:58 | eagle ov sea

# re: 只遍历出JScript对象的expando属性  回复   

@eagle ov sea
Remarks for Unselectable Attribute:
Note  Setting the UNSELECTABLE attribute to off does not ensure that the element is selectable. One example is anHTMLApplication (HTA) with theSELECTIONattributeset to no. Elements in the body of theHTA cannotbe selected, whether the element's UNSELECTABLE attribute is setto off or not. 
Clicking on an element with the UNSELECTABLE attribute set to on does not destroy the current selection if one exists.
 
An element with the UNSELECTABLE attribute set to oncanbe included in a selection that starts somewhere outside the element.
The UNSELECTABLE attribute is implemented as anexpando.Setting theexpandoproperty of thedocumentobject to false precludes the functionality of allexpandos.

2006-06-06 13:04 | birdshome

# 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
2006-08-11 15:48 | long_biti

# re: 只遍历出JScript对象的expando属性  回复   

@long_biti
我这个方法的目的是取出所有的expando属性,而你的那个setValue和getValue虽然是function类型,但是这两个方法对于其所属对象来说,也是expando属性。
你要滤掉function只是一个特殊需求,完全可以像你那样来修改,不过修改由就不是获得的所有expando了
posted on 2006-09-29 08:50  javaca88  阅读(238)  评论(0编辑  收藏  举报