热门:网页模板.net视频教程JQueryMVCjsonExtJs源码示例三级联动JQuery菜单
您现在的位置:.Net中文社区>> AJAX编程>>正文内容

ExtJS 2.2数据及集合

发布时间:2009年01月09日点击数: 彭仁夔
返回深入学习ExtJS 2.2开发系列连载教程目录
集合是存放一批数据的地方。在Java,C#等面向对象语言中一般都提供List,Set,Map这三类集合。List是有序集合,Set是无序集合,Map则是通过key/value对形成的映射集合。在这三种集合中基础上,它们还会提供一些特殊的子类,像处理同步机制等。

Javascript不是完全的面向对象,但还是实现了集合,因为它是一门语言中最基本的功能。说起JavaScript集合,读者第一反应就是数组(Array)。数组是通过下标(index)来取得其中元素,是有序集合。那么Javascript有没有无序集合和映射集合呢?有,那就是对象。Javascript中所有对象的属性或方法都是采用key/value形式无序储存着。它既是Set无序集合又是Map或Hash类型的映射集合。很多类库都是采用对象该特性来模拟Hash集合。函数,数组等对象,它们本质上也是无序集合,都可以往其内部注册key/value形式属性来保存数据。

在上一节中,讲到函数能模拟静态类,那是因为它本身就是对象,也就可以直接往函数对象中注册静态方法或属性。数组也是一样的。虽然Array的类型是函数,但是其实例(new Array或直接量如[xx,yy])则是对象类型。这也就说明了我们可以在数组实例中注册key/value属性对来保存数据。可以说数组实例即是无序集合又是有序集合。

对于无序集合。一般都是通过for p in obj的反射形式来遍历。对于有序集合,一般通过for(var i=0;i<array.length;i++)形式来遍历。取得对象中某个属性,可以通过obj[name]形式来获到。对于数组有序集合部分,该name是数字,其无序集合部分,则还可以通过obj.name来获取。这样数组其实质就是对象,其有序是通过无序集合来模拟。我们可以通过for p in obj遍历数组中的所有有序或无序集合,在遍历过程中,它会把无序集合部分中把原生属性或函数排除开来,但是对于扩展无序集合部分会一一遍历。这也就是前面所讲的对于object对象进行扩展(如extend)时,其会对反射造成影响。为了避免这种冲突,我们可以采用两种方式,一是不扩展,如给数组原型扩展each,map等方法。二是不采用for in 形式遍历,而采用for(var i=0;i<array.length;i++)来遍历。

对于object对象无序集合,我们还可以采用其key为等于数值来构建类数组对象。只要在基础之上加上一个能随元素加入删除而相对应改变大小的length属性就可以了。类数组当然没有数组中那些已经实现的方法,但是其独成一体,减少了对数组扩展造成的冲突。Jquery对象就是类数组对象。具体见笔者拙作jquery1.2.6源码分析(网络书籍)。

3.3.1 JavaScript数组
Javascript数组是有序集合。对于有序的集合,我们可以模拟堆栈、队列等操作。它的数组不像其它语言的数组,仅仅当作单纯的数组操作,它是当有序集合来看待,因为它还提供了堆栈、队列等函数的实现。数组中有一些很重要函数。下面就进行分类总结一下:

类别

函数格式

说明

堆栈

array.pop()

通过pushpop方法进行堆栈先进后出操作。push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。pop() 方法用于删除并返回数组的最后一个元素。

array.push(a1.,aX)

队列

shift()

通过shiftunShift方法进行堆栈先进先出操作。shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。

unShift(a1,.,aX)

排序

sort(sortby)

Sort()默认是按照字符编码的顺序进行排序。对于其它其他标准进行排序,就需要提供比较函数sortbya,b),该函数返回值必须是数值,以0为边界。它会对数组中所有元素进行两两比较(a,b),如果小于0a就在b的前面。如果大于0,a就在b的后面。reverse() 方法用于颠倒数组中元素的顺序。这两个函数都会改变原数组。

reverse()

串联

concat(a1.,aX)

concat() 方法用于连接两个或多个数组。该方法不会改变现有数组,仅仅会返回被连接数组的一个副本。其参数还可以是单个元素。

join()返回一个字符串。该字符串是通过把数组中每个元素转换为字符串,然后通过separator间隔符把这些字符串连接起来。

join(separator)

其它

slice(start,end)

slice() 方法可从已有的数组中返回选定的所有元素的集合。它是根据参数来指定开始和结束位置。没有结束位置就是数组的结尾。

splice()是集插入删除为一体的操作函数。其第一个参数是操作的开始位置。第二个参数是删除多少元素。之后的参数就是指定在index的位置上及之后插入的元素。

如果size0的话,就表明不删除元素。如果有a1..ax的参数就表明要插入元素。如果都有的话,就是先删除再插入,这就相当于是替换数组的元素的功能。

splice(index,size,a1,.,aX)

3.2  数组中常用函数说明

上表中归类给出了数组的常用函数的使用说明。其中join 的函数是特别重要的,这里就给一个例子来说明其用法,array.join()的用法
(function ArrayTest(){
var testString ="this is for fucntion test in array join!";
var testArr=[1,2,3,4];          
var n=['test:',testString.slice(0,25),testArr,3+5,{name:'xxx'}];
alert(n.join("-->"));}
)();    

这个函数会弹出:test:-->this is for fucntion test-->1,2,3,4-->8-->[object Object]这样的字符串。我们分析一下。它对于string类型的是不用转换。对于对象类型元素调用其toString()的方法来转换成字符,如代码中testArr数组和{name:'xxx'}对象。对于表达式和javascript的语句它采用类似eval的形式来执行它得到一个对象。之后采用这个对象的toString()的方法转换成字符串。对于数字等这些原始类型就是直接转换。

上面我们为什么说像eval一样呢?那是因为其执行javascript的语句的上下文也会eval一样。通过上面的例子,我们可以看出testString.slice(0,25)它是调用join所在域的上下文中的testString。Join的这个功能是非常强大的。而且极为灵活。

上面给出数组的常用函数,这些函数功能是很强大,但是还是有很多不足之处。首先它就不能根据指定的元素来获得其在数组中的位置(index)。如果没有找到位置如果采用返回-1之类的值。第二个就是它没有提供each对每个元素都进行类似的操作,这个类似的操作是通过函数来进行,这是每个类库都会提供的功能。第三是对数组中每个元素提供map操作。很多时间都会用到把一个数组通过函数转换成另外一个数组。

其实这些功能在javascript1.6中已经提供,但是有一些主要浏览器并没有实现。所以也就是没有用。Ext对数组进行了扩展。首先它扩展了indexof来给某个元素在数组中定位。第二它建立在indexof和splice之上提供了一个删除指定元素的remove函数。splice只有删除指定位置的元素。

对于数组的each方法。Ext通过Ext.each(arr,fn,scope)方式提供了一个静态方法。这部分源码比较简单,不列出。

3.3.2 ExtJS集合操作
在上一小节中,我们看到Ext对数组的操作扩展了几个实用的函数。这样的数组的功能就加强了很多。但是仔细想一想还是不够。它没有提供map或some这样的函数进行数组的过滤或转换。这是很重要的。

再进一步考虑,有时候我们会在删除元素时候提示确定的信息,对于数组元素的删除有的时候在删除文件之前可能要经过一些特殊。这些特殊的处理只是用户来定义,并注册回调函数,像注册事件处理函数那样。也就是在有的需求会要我们在对集合进行操作的(如update,remove,add clear等)时候加上事件处理。

有的时候,我们还想如何一个集合即能保持key/value的无序的map集合元素,又有保持数组形式的有序集合多好。它们两者的元素既可以通过index的数组方式来访问,又可以通过key/value来访问(如果有的话)。

Ex的Ext.util.MixedCollection类就实现上面的所要的功能。它不但提供了事件的处理,还能混用index和key索引的有序和无序的集合。Ext.util.MixedCollection类继承于Ext.util.Observable类,也就是继承于事件处理的功能。它提供"clear", "add", "replace","remove", "sort"这五种事件,让用户能注册其监听函数。对于这一部分到Ext.Event的章节进行讲解。

MixedCollection实例是一个集合对象,不是数组,他如何实现类数组的特性呢?对象与数组的区别在于数组提供了一个length属性。这个属性能根据增加,删除元素而自动地相对应地修改其length的值。只要实现这个,就可以采用对象模拟集合,或者说是数组。这里它采用了array-like实现方法。为这个MixedCollection提供了一个this.length属性,并且这个属性能在增加元素的时候进行相对应的自动增加值,在删除时能减少值。

对于length属性的自动增差问题,只要在初始化这个对象时复位为0。在调用插入或增加元素的函数时候,采用this.length++,调用删除元素函数的时候,采用this.length--。并且保证其不超界就可以了。

对于混用index和key索引的有序和无序的集合的话,它采用this.items = []; this.map = {}; this.keys = [];三个集合来协同实现这个功能。对于所有的index或key方式的元素都把它存在items集合中。与之一一对应的是keys集合。这个集合是保存其key。对于index方式的元素,就采用null做为其key。Itmes和keys的元素是一一对应的,就是删除的时候也要一一对应地删除。

这样的话,我想通过index的方式取得元素。只要通过items就可以取到所有的不管是Key/index索引的元素。因为key索引的元素现在在itmes数组中也有了位置。对于想通过key来取得其索引的元素的。只要在keys数组中找到它的位置,再根据这个位置去到items数组中就可以直接取到。

好像忘记了this.map哦,它们是协同工作,怎么少了它,从上面看,有没有它也没有什么关系。但是Ext为什么多此一举呢?在通过key找与之对应的元素的话,先要在keys的数组中找到key,之后还要到items数组中去取。我们分析其效率,在keys的数组中一个个地比较key,最终找到其index。这个效率是很低的。最坏的情况是所有元素遍历。还要进行比较的时间。通过index在数组中找元素,采用二分法对于2∧64的大小的集合只要64次就可以查找到,,这个是很快。

现在得讲讲this.map集合了,在增加key/value的形式的元素时,它也还把其加入the.map中。采用对象的无序集合来保存。这样看来是肯定多占用了内存。因为数组对元素保存只是引用。这部分的内存占用不大。但是通过this.map[key]的方法来查找元素肯定比先找到key,再通过key去定位快。浏览器的这些原生的实现是采用C++等底层的语言,效率比indexof的Javascript的效率要高很多。同时很多浏览器都会采用一定的算法来优化这种查询。在这里是通过空间换时间。

关于其类数组的实现,这里为什么不采用jquery的那种通过this[]的方式来实现有序和无序的统一呢?对于有序的采用index做为key。一是对于对象的key/value方式查找的效率肯定比数组差。第二如果无序的元素的key为数字怎么办,很有可能会造成覆盖或冲突。

Ext中对于集合的使用,如Component管理器,Store管理器等都是采用Ext.util.MixedCollection来作为其集合的。它比数组提供物了更多的便利的方法。在Ext的文档中都有说明。但是没有进行分类。这里把它进行分类,便于记忆。

分类

函数名

说明

add

add

addall

insert

add类主要功能是实现把数据存放到集合中。add是单个元素的增加,而addall是增加一批元素,它提供了多参数,object属性key/value,数组的三种方式传入多个元素。insert则是在指定的位置上插入元素。

remove

remove

removeat

removekey

clear

remove类是清除数据的操作。removeremoveatremovekey是分别的从valueindex,key三个角度去找到相对应的元素,之后删除。items,map,keys中都会删除。clear是完全清除所有的元素。

index

indexof

indexofkey

findindex

findindexby

index类是找到给定的数据的定位。indexof是通过value的参数在items的集合找到其位置。而indexofkey则是根据keykeys中找到其位置。

findindex是根据regexp和指定key/index/value三种方式之一在集合中找到第一个满足的位置。而findindexby则是通过函数找到满足条件的第一个元素的位置。

find

find

first

last

item

itmeat

key

getrange

find是根据函数找到满足的第一个元素。而first,last则是集合中第一个元素或最后一个元素。key是通过keymap中找到对应的元素。itemat是通过index找到集合中元素。item是综合key/itemat的函数,先通过keymap中找,没有找到就通过indexitems中找。

getrange是给定开始位置和结束位置用集合找到一批元素。如果结束位置小于开始位置,就会逆序集合中找,返回逆序集合

filter

filter

filterby

filter类是提供一组关于过滤的操作。通过key/value/indexregexp的条件来筛选取集合。filterby则是通过函数的形式找到满足条件的集合。注意它们是另外生成一个MixedCollection集合,对他们的改成,不会改变原集合中的数据。

sort

sort

keysort

_sort

sort类是对集合中数据进行排序的操作,和array中的sort差不多。不过它提供了一个desc,asc这样的升序还是降序的操作。keysort则是通过key的方式来排序整个集合。其源码在第5章分析

other

getcount

这是一种统计的函数。getcount取到data长度。

each

eachkey

each是对集合中每一个元素都进行fn类似的操作。eachkey则是对keys集合中每个key进行fn类似的操作。

replace

替换集合的某一个元素。

contains

containskey

contains判断集合中否包含指定的元素。containskey则是判断集合是否包含这个指定key对应的元素。

3.3  MixedCollection中的方法


在办公系统中,我们首先要做的事情是对于公司全部的员工进行统计,那么每个部门都要向计算机系统提供其人员相关信息,在这里我们就采用.MixedCollection做为客户端的存储数据地方。

在使用MixedCollection之前,我们得通过new Ext.util.MixedCollection()来生成一个实例。它有二个参数,第一个是allowFunctions用来判断添加的元素是否允许是函数元素,默认是不允许,因为在使用反射时会混杂元素名和集合的操作函数名。第二个参数是定制集合中元素的标识函数,如果取得该集合中元素的key。可以为空。对于员工来讲,其标识可以定制为员工编号。

那么我们就采用如下方式生成集合:

var getkey=function(o){return o.ygbh}; var c= new Ext.util.MixedCollection(false,getkey);

下面就添加数据,假如信息部提供的数据如下:

var prk={ygbh:3306,name:prk,age:24,sex:1};

var pch={ygbh:3307,name:pch,age:30,sex:0};

对于这样的数据,我们可以通过add的方式来添加加到集合中去。

c.add(prk);c.add(pch);

add函数是用来把单条数据增加到追加到集合中去。我们可以只指定一个参数,即元素,采用默认的生成的key。上面采用是ygbh。我们还可以采用指定key的形式,这样第一个参数就是指定的key,如我们想采用name做为prk对象的key。那么就可以通过c.add(‘prk’,prk)。

add函数在执行过程中,先判断其参数的个数,如果指定key就采用该key,没有指定就通过getkey函数来获得默认的key。如果这两种方式都没有找到key话,那么就把元素存到items集合中,与之对应的key是null来占有其对应的keys集合中的位置。同时类数组的length会加上1。

如果找到key(这个key不是null或undefined),那么就得先通过map无序集合来找到是否在它这里注册过。如果有的话,说明items元素集合中已经存在,那么就进行替换原来的元素。这里也就是如果我再一次c.add(prk),那么不会出现两个prk的记录。不存在的话,就添加到集合中,与无key的添加有一点不同,它也要map的无序集合中通过key/value又的形式保存该对象的key与元素的对应关系。其主要是便于查找。

这样一个个地添加数据是有点麻烦,于是在收集财务部和人力部的数据时,就采用了集合的形式。

人力部:var hr=[{ygbh:3406,name:wp,age:35,sex:1},{ygbh:3407,name:wxm,age:37,sex:0}]

财务部:var ec={3506:{ygbh:3506,name:lc,age:45,sex:1},3506:{ygbh:3507,name: lx,age:28,sex:0}}。

这是两种不同的集合。一个是通过数据的形式,一个是通过对象的形式。添加这的数据,我们可以通过addAll函数。c.addAll(hr);c.addAll(ec);addAll通过其名字就可以知道它用来是增加集合形式的数据。对于集合的形式的数据,它采用默认的key的生成方式,而对于对象形式的数据,它采用属性名作为其key,为了和所有key统一起来,我们采用其员工编号值作为属性名。这是要注意的地方。

添加完了数据之后,我们发现prk员工性别在收集时不小心弄错了,应该改过来。这个通过add的方式也可以修改,不过最好是通过replace函数来进行修改。现在prk的数据如下:prk1={ygbh:3306,name:prk,age:24,sex:0};。我们可以通过c.replace(prk1)就可以进行替换原来错误的Prk的数据。

再之后不久,信息部来了一个新的员工,我们也把她加到信息部员工顺序中去。这个通过insert来实现。在insert的时候,我们要找到信息部所在的位置。对于定位,有四个函数,如果我们想在pch后面插入的话,那么就可以通过indexof(pch)来找到它的位置,之后在这个位置上加上1,就是插入新员工的位置。如果仅仅是知道pch的key,也可以通过indexofkey(3307)来找到pch的位置。

其实我们要找到编号为33的任何一个位置都可以,这个可以通过findindex来实现:

c. findindex(‘ygbm’,33,0,true,false)就可以找到第一个信息人员的位置。在它之后就可以插入。如果觉得这样还是不好的话,想找到ygbm为33而且name为prk的员工,只能通过findindexby通过函数的形式来自定义找到:

c. findindexby(function(o,k){if(o.name.match(‘prk’)&&k..match(33)) return true;},null,0);

所有数据都存好了。就可以在需要时进行使用。

本站热点业务

更多模板/案例展示

关于我们 | 联系我们 | 团队日志 | 网站地图 | 网站合作