原创作者: damoqiongqiu   阅读:3349次   评论:2条   更新时间:2011-05-26    

 

导言

         差不多所有的 js 库都会对原生的 js DOM 模型进行封装和扩展, jQuery prototype Ext 都有类似做法。

         由于这些原生对象的相关方法注定会被大量地调用,对它们扩展得好与不好会直接关系到整个类库整体的性能。

         经常看到有人说 Ext 的执行效率低下,笔者有时也有这种感觉,点个鼠标好像要延迟一下才有反应。但是说话得要有根据不是,不知道慢在哪里也不能跟着瞎起哄。(当然,也免得文章发出来被别人拍板砖,已经被拍过几次,汗啊 )。

         前一部分文章说了一下怎么用 VML 直接在网页上绘图,并且封装了一个小工具。这一部分的内容就先来看一下 Ext js 的原生对象是如何封装和扩展的。

 

 

概览

        

 

         从截图可以看到,在 Ext.js 这个文件中, Ext Function String Number Array Date 。五个原生对象进行了扩展。这一篇先来分析一下对 Array 对象的扩展,并提出一个小小的疑惑。

 

Array 对象

         js 的原生 Array 对象包含一个 length 属性和 12 个方法,简表如下:

         concat() :添加元素

         join() :将数组中的元素连接成一个字符串并返回

         pop() :弹出最后一个元素

         push(): 把一个元素加到数组最后

         reverse(): 颠倒顺序

         shift(): 移除第一个元素

         slice() :返回子数组

         splice(): 插入、删除或替换一个元素

         toLocaleString(): 把数组转换成一个本地化的字符串

         toString() :把数组转化成一个字符串

         unshift() :在数组头部插入一个字符串

 

         以上引自《 JavaScript 权威指南 第五版》,加粗部分是自己对照英文版的的翻译,原来的翻译是“把数组转换成一个局部字符串 ”,意思非常模糊,让人搞不懂。顺便再罗嗦一下,第五版的翻译不知道是干什么吃的,很多的东西不翻译或者直译,漏洞百出,建议想买这本书的人不要买这个版本。

        

Ext.js 里给 Array 对象扩展了两个方法,代码如下(注释已经翻译成中文):

Ext.applyIf(Array.prototype, {

    /**

     * 查找数组中是否存在指定的对象

     * @param {Object} 需要查找的对象(的引用)

     * @return {Number} 对象在数组中的位置下标 ( 如果不存在该对象,返回 -1)

     */

    indexOf : function(o){

             // 注意这里:是简单的线性查找,拿数组中的元素一个一个地去对比。

       for (var i = 0, len = this.length; i < len; i++){

               if(this[i] == o) return i;

       }

            return -1;

    },

        

    /**

     * 从数组中删除指定的元素,如果该元素不存在,则什么事情都不做。

     * @param {Object} o 需要删除对象的引用

     * @return {Array} 返回删除后的数组

     */

    remove : function(o){

       var index = this.indexOf(o);

       if(index != -1){

           this.splice(index, 1);

       }

       return this;

    }

});

 

在注释里面加注意的地方说到 indexOf() 方法的查找问题,可以看到,这里的 indexOf() 使用的是简单的线性查找, remove() 方法调用了这个 indexOf() 方法来查找对象在数组中的位置。这个地方的效率很低,也就是说,如果我们往一个数组里面保存了很多的对象,然后在另外的地方频繁调用数组的 indexOf() 或者 remove() 方法的时候,速度就会比较慢。所以有人老说 Ext 存在效率问题,我想,这个所谓的“效率问题”不是哪一处地方造就的,而是由诸多效率差的小细节积累而成的,假如我们实在没有办法去改变什么,那么至少要注意到这些地方,避免陷阱,这是这篇文的主旨所在。

由于线性查找效率是 O(n) ,所以,在数据量稍大的时候,需要寻找替代 Array 的办法。有很多文章说过关于 Array 的这个问题,包括《权威指南》,办法是模拟一个 Hash 表。基本的思路如下:

var mp={};

         mp['key1']={name:'damoqiongqiu',age:'25',QQ:'88403922'};

         mp['key2']={name:'damoqiongqiu',age:'26',QQ:'88403922'};

         alert(mp.key2.name);     

         这样处理,可以提升查找效率,效率可能接进 O(1) 。(顺便说一句,效率只能接进是 O(1) ,因为 js 内部通过这个字符串型的 key 去找到 value 也是需要花时间的,即使是通过类似Java或C++的Hash算法,也是需要通过key来算HashCode的不是?通过HashCode找到对应的Hash桶之后还是一个线性查找。)

        

         《指南》里面还有提到一种伪数组:

         var a={}

         while(i<10){

                  a[i]=i*I;

                   i++

         }

         a.length=i;

        

         如果情况比较好,事先知道插入的元素是有序的就可以使用其它的查找算法,像二分查找之类的,这样的情况估计不容易遇到,除非上天恩赐 J ,否则还讨论个什么劲?

        

         当然,有些情况必须用到数组,除此没有办法,那还有什么好说的,用呗!但是,在确确实实对效率有很多要求的地方,需要考虑类似的变通方法。

 

一个疑惑:

经常看到有人问, js 里面的 == === 有什么区别,翻了一下书,大概的意思是:

=== 是比较两个值是不是完全相等。

而如果用 == 来比较,假如两个值是同一类型,就用 === 比较它们是否等同。否则如果两个值是以下组合时,将非 Number 型强转成 Nunber 再来比较。

String Number

Number Boolean

也就是说, ”1”==true 是正确的, 1==true 也是正确的,如此这般。

如果一个值是对象,另一个值是 String 时,将对象转成 String ,再来比较这两个 String 是不是相等。

 

         OK ,有了上面的简单解释,我们来测试一下 Ext Array indexOf() 方法。

         首先来个简单的:

         Ext.onReady(function(){

         // 基础语义测试

         var test="1";

         alert(test==true);

})

没有问题,出来一个 true

 

再来个稍微复杂点的,先向数组里面插入 1000 个对象,然后在第六个元素放一个布尔型的 true 值,然后自己来遍历数组,看看有没有元素和字符型的 ’1’ 相等:

Ext.onReady(function(){

         // 自定义数组测试

         var test="1";

         var arr=[];

         for(var i=0;i<1000;i++){

                   var temp=(i==5)?true:{id:i};

                   arr.push(temp)

         }

         for (var i = 0, len = arr.length; i < len; i++){

             if(arr[i] ==test){

                      alert(i);

                            break;

             }

    }

})

好,没有问题,找到了第六个元素。

 

来测 Array indexOf() 方法,从 Ext 的源码里面可以看到, Array indexOf() 方法和我们上面自己遍历的方法是一样的,但是,神奇的事情发生了,看代码:

Ext.onReady(function(){

         // 自定义数组测试

         var test="1";

         var arr=[];

         for(var i=0;i<1000;i++){

                   var temp=(i==5)?true:{id:i};

                   arr.push(temp)

         }

    // 测试 Ext 封装 Array indexOf() 方法

         alert(arr.indexOf(test));

})

按道理来说,这里也应该能够弹出来元素的位置,但是,结果是,没有找到这个元素。。。可视,这是为什么呢?期待牛人给出解释,不甚感谢!


 


这个问题已解决,多谢zbm2001的解释,我自己又测试了一下,只有FireFox实现了Array原生的indexOf()方法,而IE、Safari、Opera都没有原生实现。Ext在扩展的时候用的是applyIf()方法,所以在存在原生实现的时候不会拿Ext的实现去覆盖。

评论 共 2 条 请登录后发表评论
2 楼 damoqiongqiu 2010-11-30 20:14
呃,,,居然被转到了这里。
1 楼 liusu 2010-06-06 22:27
indexOf原生实现? 呵呵。 浏览器差异很恐怖啊。

发表评论

您还没有登录,请您登录后再发表评论

文章信息

Global site tag (gtag.js) - Google Analytics