• 框架设计—选择器模块


    一个礼拜没动静了,实在是懒惰。。

    好了,不扯淡了,进入正题:框架封装之选择器模块。

    首先,我们为什么要封装框架?

    浅显的文字不具有良好的说服性,来做几个题目吧:

    1. 求一个数组所有项之和

    //求和
     var sum=0;                      
     for(var i=0; i<arr.length;i++){ 
         sum+=arr[i];                
     }                               
      console.log(sum);             

      2. 求数组中最大值

    //求最大值
    var
    max=arr[0]; for(var i=0; i<arr.length;i++){ if(max<arr[i]){ max=arr[i]; } } console.log(max)

     3. 获取数组中指定值

    //获取指定值                 
    var index=0;              
    function get(v){          
        for(var i in arr){    
            if(arr[i]===v){   
                index=i;      
                break;        
            }                 
        }                     
    }                         

    ok,题目做到这就行了,我们可以看出每道题目中都用到了for循环,每次都写一遍是不是很麻烦,这里代码不多,可能感觉还好,如果工作量大,冗余度是很高的,所以我们就可以封装这个for循环:

    //  arr: 目标数组                                                          
    //  fn: 自定义方法                                                         
        var each= function ( arr , fn ) {                                   
           for(var i=0; i<arr.length;i++){                                  
               // 使用call改变作用域,后面调用可直接使用this                                 
               if( fn.call(arr[i],i,arr[i])===false) break;                 
               //做搜索操作,break必须写到循环中,此处使用返回值检索是否执行break                      
           }                                                                
       }                                                                    

    封装后重写求和方法

      //1.封装后求和                 
      var sum=0;                
      each(arr,function(i,v){   
         sum+=v;                
     })                         

    好了,其他就不写了,比较简单,所以框架主要是为了降低程序之间的依赖性和耦合性,使重用性达到最高,下面说说我们的重点,如何封装选择器框架。

    目标:实现常见选择器框架的封装,即id选择器,类选择器,标签选择器。

    //传统做法
    function getId(id){
            return document.getElementById(id);
        }
    function getTagName(name){
            return document.getElementsByTagName(name);
        }
    //获取id为box的节点,然后操作
    var dom = getId('box');
    'red';
    dom.style.background = 'red';
    //获取属性名为div 的节点,然后操作
    var tags = getTagName('div');
    for(var i=0; i<tags.length;i++){
      tags[i].style.background = 'red';
    }

    传统做法如上,可以通过该方法实现普通需求,但是要是继续想通过类名,标签名获得属性,然后分别加样式,岂不是很累,所以我们可以通过下面方式取代原始方法:

    //id 选择器
        function getId(id,node,arr){
             arr.push(node.getElementById(id));
            return arr;
        }
    // 属性选择器
        function getName(name,node,arr){
            [].push.apply(arr,node.getElementsByTagName(name));
            return arr;
        }
    // 类选择器
        function getClass(clas,node,arr){
            [].push.apply(arr,node.getElementsByClassName(classname));
            return arr;
        }

     紧接着我们再实现一个接口, 根据输入,调用不同的选择器

    //多选择器接口
    //    getType( tag , node , arr )
    //                类型   父节点  数组容器
    
        function getType(tag,node,arr){
            node= node || document;
            arr= arr || [];
                  type(1) type(2) type(3) type(4)
    var rag=/^(?:#([w-]+)|.([w-]+)|([w-]+)|(*))$/; var type=rag.exec(tag); if(type){ if(type[1]) arr=getId(type[1],node,arr); else if(type[2]) arr=getClass(type[2],node,arr); else arr=getName(type[3]||'*',node,arr); } return arr; }

    上文中用到了正则表达式,简要解释下,/^(?:#([w-]+)|.([w-]+)|([w-]+)|(*))$/

    ^表示已xx开头,$表示已xx结尾; 

    (?:)其中'( )'表示分组,此处使用只是提升中间部分优先级,使用 ?: 取消分组; 

    匹配 id:#([w-]+)  可通过  type(1)  获取除#外的id字符

    匹配 类:.([w-]+)    type(2)

    匹配 标签名:([w-]+)        type(3)

    通配符:(*)        type(4)

    PS:
    var arr1=[1,3,4];
    var arr2=[3,4,5];

    如果我们要把 arr2展开,然后一个一个追加到arr1中去,最后让arr1=[1,3,4,3,4,5]
    arr1.push(arr2)显然是不行的。 因为这样做会得到[1,3,4,[3,4,5]]

    但是我们可以用 Array.prototype.push.apply(arr1,arr2) 实现需求
    因此我们这样做 [].push.apply(arr,node.getElementsByTagName(name));

    但是,在IE8 及低于IE8以下的浏览器需要注意几个问题.

    1、 apply 传参不接受类似 {0:'a',1:'b',length:2} 的对象,可以是 数组、arguments、  HTMLCollection 对象 和 Nodelist 对象等节点集合.

    在这种情况下你也许想要把传参对象转换成数组.

    2、节点集合无法调用数组的原型方法,但是 类似 {0:'a',1:'b',length:2} 的对象可以。

     看似我们实现了所有需求,但是别忘记另我们头疼的IE8浏览器,它不支持 getElementsByClassName 为此我们需要解决兼容问题。
    您也许会想到以下方法:
     if(document.getElementsByClassName){
            var results= document.getElementsByClassName('box');
        }
        else {
            //自己实现的方法
        }
    
    
    
    但是存在两个问题 1.性能问题 2.安全问题
    先解决性能问题 :我们都知道原型链吧,当函数或对象执行某方法或使用属性时,会从其自身开始搜寻,然后一直沿着原型链往上找,直至找到为止才停止寻找,每往上一层,都会增加搜寻时间,降低性能,getElementsByClassName是
    document的属性方法,因此最好将该属性放在当前对象中,而且每次执行时都会判断是否兼容,也降低性能,因此我们可以用support对象缓存能力检测结果,如下
    1.提升性能
     var support={};
    //!! 装换成boolean类型
    support.getElementsByClassName =!!document.getElementsByClassName;
       if(support.getElementsByClassName){
           var results= document.getElementsByClassName('box');
       }
       else {
           //自己实现的方法
       }
    
    
    
    减少了性能问题,但是存在安全问题 ,看下面代码会输出什么?
    document.getElementsByClassName=123;  //注入代码攻击
        var support={};
        support.getElementsByClassName = !!document.getElementsByClassName;
        if(support.getElementsByClassName){
            console.log('支持ByClassName');
        }
        else {
            console.log('不支持ByClassName');
        }
    
    
    
    无论在什么浏览器中都会输出: 支持ByClassName.
    因为document.getElementsByClassName=123;这句代码,此时执行判断是true,具体传送门==>http://blog.csdn.net/writehappy/article/details/8970491
    告诉你们一个大牛们使用注入代码攻击的做的事:在chrome上为自己的微博刷赞!!!
    好了,转回整体,如何解决这个问题呢?
    继续看操作:
    2.提升安全性
    //    在全局作用域(最终要变成沙箱模式),提供一个support对象,存储属性是否支持,不用每次检测
            var support={};
            support.getElementsByClassName = (function () {
                istrue= !!document.getElementsByClassName;
                if(istrue&& (typeof document.getElementsByClassName==="function")){
                    //不仅判断是否支持该方法,还要执行该方法检验
                    var div=document.createElement('div'),
                            testDiv=document.createElement('div');
                    testDiv.className='test';
                    div.appendChild(testDiv);
                    //检验
                    return div.getElementsByClassName('test')[0]===testDiv;
                }
                else return false;
            })();
    
    
    
    很棒吧,jquery就是这么实现的,以下
    jquery实现
    /**
     * Support testing using an element
     * @param {Function} fn Passed the created div and expects a boolean result
     */
    function assert( fn ) {
        var div = document.createElement("div");
    
        try {
            return !!fn( div );
        } catch (e) {
            return false;
        } finally {
            // Remove from its parent by default
            if ( div.parentNode ) {
                div.parentNode.removeChild( div );
            }
            // release memory in IE
            div = null;
        }
    }
    
        // Support: IE<8
        // Verify that getAttribute really returns attributes and not properties
        // (excepting IE8 booleans)
        support.attributes = assert(function( div ) {
            div.className = "i";
            return !div.getAttribute("className");
        });
    
        /* getElement(s)By*
        ---------------------------------------------------------------------- */
    
        // Check if getElementsByTagName("*") returns only elements
        support.getElementsByTagName = assert(function( div ) {
            div.appendChild( doc.createComment("") );
            return !div.getElementsByTagName("*").length;
        });
    
        // Support: IE<9
        support.getElementsByClassName = rnative.test( doc.getElementsByClassName );
    那么我们的类选择器可以重新写了
    类选择器重写
     function getClass(clas,arr,node){
            arr=arr||[];;
            [].push.apply(arr,getclass(clas,node));
            return arr;
        }
            //getClas兼容方法
            function getclass(classname,node){
                node=node || document;
                if(support.getElementsByClassName){
                    return node.getElementsByClassName(classname);
                }
                else {
    //      2.自己实现的代码
                    var arr=document.getElementsByTagName('*');
                    var results=[];
                    each(arr, function (k,v) {
                        if(this.className===classname){
                            results.push(this);
                        }
                    });
                    return results;
                }
            }
    
    
    
     实现效果:


    ok,这次就讲到这儿吧,啰啰嗦嗦,希望各位不要嫌弃我的这点拙见,还希望多多指点,共同进步,未完待续。
    2016-04-10
  • 相关阅读:
    Linux双线双网卡双IP双网关设置方法
    Docker 清理命令集锦
    Centos7安装Docker 基于Dockerfile 搭建httpd运行环境
    Centos6.x 安装vnc
    KVM虚拟化技术
    ELK监控系统nginx / mysql慢日志
    ELK初学搭建(elasticsearch)
    (转)Linux 磁盘IO性能测试
    hadoop2.9.2 调整jvm
    (转)shell调试方法
  • 原文地址:https://www.cnblogs.com/delicate/p/5376623.html
Copyright © 2020-2023  润新知