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

=====================================================================

 

#3楼    回复  引用  查看    

程序有问题。

第三行的 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 | 宇义 [未注册用户]

#4楼 [楼主]   回复  引用  查看    

@宇义
谢谢提醒,已修正:)
    你的这个改进因该说还是不错的,效率上也不会有太大的差别。不过这里有个问题,我们在使用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

#5楼    回复  引用  查看    

只有在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 | 宇义 [未注册用户]

#6楼 [楼主]   回复  引用  查看    

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

#12楼    回复  引用  查看    

什么叫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 [未注册用户]

#13楼 [楼主]   回复  引用  查看    

@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 the SELECTION attributeset 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 the expando property of the document object to false precludes the functionality of allexpandos.


2006-06-06 13:04 | birdshome

#14楼    回复  引用  查看    

有一些地方还是不明白,比如我在这里定义的一个类(另外一篇的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: [email protected]
2006-08-11 15:48 | long_biti [未注册用户]

#15楼 [楼主]   回复  引用  查看    

@long_biti
我这个方法的目的是取出所有的expando属性,而你的那个setValue和getValue虽然是function类型,但是这两个方法对于其所属对象来说,也是expando属性。
你要滤掉function只是一个特殊需求,完全可以像你那样来修改,不过修改由就不是获得的所有expando了。
2006-08-12 19:05 | birdshome

#16楼    回复  引用  查看    

楼主的意思是要在for in时不包括prototype已有的东西吧?
那最直接的方式就是:
for (var x in o)
if (o.hasOwnProperty(x))
... // x 不在 prototype 中
2007-02-21 22:51 | 5678 [未注册用户]

#17楼    回复  引用  查看    

就是用这个:hasOwnProperty
2007-06-26 13:06 | CaiKanXP [未注册用户]

#18楼 [楼主]   回复  引用  查看    

@5678
yeah, 就是这个,3ks。用hasOwnProperty确实更专业些:)
function  GetExpandoValues(map)
{
    
var values = [];
    
for ( var key in map )
    
{
        
if ( map.hasOwnProperty(key) )
        
{
            values.push(map[key]);
        }

    }

    
return values;
}
2007-06-26 13:26 | birdshome

JavaScript中hasOwnProperty函数

JavaScript中hasOwnProperty函数方法是返回一个布尔值,指出一个对象是否具有指定名称的属性。

使用方法: object.hasOwnProperty(proName)
其中参数object是必选项。一个对象的实例。
proName是必选项。一个属性名称的字符串值。

如果 object 具有指定名称的属性,那么JavaScript中hasOwnProperty函数方法返回 true;反之则返回 false。
此方法无法检查该对象的原型链中是否具有该属性;该属性必须是对象本身的一个成员。
在下例中,所有的 String 对象共享一个公用 split 方法。下面的代码将输出 false 和 true。
var s = new String("JScript");
print(s.hasOwnProperty("split"));
print(String.prototype.hasOwnProperty("split"));

你可能感兴趣的:(只遍历出JScript对象的expando属性)