zoukankan      html  css  js  c++  java
  • JavaScript 框架设计(二)

    JavaScript 高级框架设计 (二)

    上一篇,JavaScript高级框架设计(一)我们 实现了对tag标签的选择

    下来我们实现对id的选择,即id选择器.

    我们将上一篇的get命名为getTag(),然后再编写一个getId(),和getClass()

    然后在总的get方法中调用,这样做的好处就是模块化,便于维护.
    我所有的代码都会托管到github上.

    01.js

    var getId = function (id, result) {
        result = result || [];
        //  由于获取的Id是一个元素,所以这里使用call.
        result.push.call(result,document.getElementById(id));
        return result;
    }
    
    var getClass = function (className, result) {
        result = result || [];
        result.push.apply(result, document.getElementsByClassName(className));
        return result;
    }
    

    好了,现在分别实现了三个get方法,但是怎么根据选择器表达式来调用者三个方法呢?


    下面引出一个关键正则:

    /^(?:#([w-]+)|.([w-]+)|([w]+)|(*))$/
    

    这段正则目的就是匹配id,类和标签选择器,并且通过exec可以拿到匹配的内容.

    那么,

    var m = expr.exec(selector);
    

    m[1] 就表示id选择器,m[2]就表示类选择器,m[3]表示标签选择器,m[4]表示通用选择器

    下面的代码就顺理成章了.

    01.js

    var get = function (selector, result) {
        result = result || [];
        var rquickExpr = /^(?:#([w-]+)|.([w-]+)|([w]+)|(*))$/,
            m = rquickExpr.exec(selector);
        if (!m) {
            return result;
        }
    
         if (m[1]) {
             result = getId(selector);
         } else if (m[2]) {
             result = getClass(selector);
         } else if (m[3]) {
             result = getTag(selector);
         } else if (m[4]) {
             result = getAll(selector);
         }
        return result;
    };
    

    如果没有匹配到,就直接返回result,如果有匹配到,那么就一定是选择器中的一种,通过分组进行判断.

    在对每个分组的判断可以优化,

     if (m[1]) {
          result = getId(m[1], result);
      } else if (m[2]) {
          result = getClass(m[2], result);
      } else {
          result = getTag(m[3] || '*', result)
      }
    

    现在这样就已经可以完成对单独选择器的匹配了.

    我们来进行测试一遍.

    01.html

    <body>
        <div>1</div>
        <div id="k">2</div>
        <div class="pawn">3</div>
        <p></p>
        <p></p>
    </body>
    <script src="01.js"></script>
    

    01.js

    onload = function(){
        each(get("p"),function(){
            this.style.backgroundColor = "green";
        })
        each(get("#k"),function(){
            this.style.backgroundColor = "red";
        });
        each(get(".pawn"),function () {
            this.style.backgroundColor = "pink";
        })
    }
    

    下面是结果,是不是欧了?

    单独的选择器似乎已经完成,但是一个问题出现了? 有考虑过兼容性吗?

    document.getElementsByClassName() ie8以下是不兼容的!

    下面引入两个问题:

    • 怎么判断兼容性?
    • 怎么解决不兼容?

    怎么判断兼容性?

    我们通过chrome watch写上document

    然后查看getElementsByClassName() 方法.

    经过不断在原型链中搜索,

    我们发现其嵌套很深,每次调用都在原型链中搜索.

    当我们这样的代码在IE8 以下运行这样的代码时,

    if(docuemnt.getElementsByClassName(className)){
      //
    }
    
    

    它会一直向上寻找,直到Object对象,效率很低.

    如果按照上面的代码,那么每调用一次就要就行一次能力检测,效率肯定太低了.

    怎么办呢?

    方法就是只做一次能力检测,并把它记录下来.

    其中,jQuery中就是这么做的,它拥有一个专门的功能检测模块,$.support,里面的属性包含存在兼容问题的方法或属性,并用布尔值来记录.

    所以在我们这里,在全局作用域中(最终都要用成沙箱或者闭包)提供一个support对象,里面提供所有的以方法名相同的属性,值均为布尔值,在浏览器加载js的开始的时候,就进行能力判断,凡是涉及到能力检测的时候就直接检测support即可.

    所以我们在代码开头加入

    var support = {};
    support.getElementsByClassName = !!document.getElementsByClassName;
    

    那么以后再需要能力检测的时候,可以这么写:

    if(support.getElementsByClassName){
        //...
    }
    

    似乎已经可以了.

    但是,有一种攻击叫做注入漏洞攻击,我在文件开头嵌入这样的代码(chrome是允许内嵌js代码的!!!):

    document.getElementsByClassName = 'pawn';
    

    那么上面的检测还有用吗?

    看看jQuery怎么做的,不仅要判断它是否存在,还要判断其能力是否符合要求.

    看下面的代码

    var support = {};
    support.getElementsByClassName = function () {
        if (!document.getElementsByClassName) {
            return false;
        }
        var div = document.createElement('div'),
            pWithClass = document.createElement('p');
        pWithClass.className = 'pawn-pawn-pawn';
        div.appendChild(pWithClass);
        var res = div.getElementsByClassName('pawn-pawn-pawn');
        return res[0] === pWithClass;
    } ();
    

    然后我们在chrome控制台输入

    support.getElementsByClassName
    

    在ie8里,

    可以看到,这样很有用.

    现在查看jQuery源代码

    看它的方法,实在是太简洁了,jQuery留给我们的,就是敬仰!!

    现在检查已经完了,怎么解决不兼容问题呢?


    解决兼容

    怎么解决呢? 首先进行能力检测,如果有,就直接调用,如果没有,实现了一个自己的myGetClassName()方法.

    原理很简单,通过通用选择器查找到所有的元素,然后看它有木有这个类名,如果有就push.

    05.js

    var getClass = function (className, result) {
        result = result || [];
        // 首先判断我们的docoument.getElementsByClassName() 有没有这个功能
        var res;
        
        if (support.getElementsByClassName) {
            res = document.getElementsByClassName(className);
        } else {
            // 自己实现getElementByClassName
            // 思路 : 首先获得所有元素,然后再在所有元素中获得带有这个类的元素
            res = myGetByClassName(className.document);
        }
        result.push.apply(result,res);
        return result;
    }
    
    
    var myGetByClassName = function (className, context) {
        var elements = context.getElementsByTagName("*"),
            res = [];
        // 循环判断是否符合要求
        each(elements, function () {
            // 这个细节非常重要 !!!!!
            if ((" " + this.className + " ").indexOf(" " + className + " ") != -1) {
                res.push(this);
            }
        })
        return res;
    }
    
    

    这个细节非常重要!!

     if ((" " + this.className + " ").indexOf(" " + className + " ") != -1) {
                res.push(this);
      }
    

    好了,我们已经添加了自己的getClassName() 方法,现在来测试一遍.

    05.html

    <body>
        <div class="dv1">1</div>
        <div class="dv1">2</div>
        <div>3</div>
        <p clss='p1'>4</p>
        <p clss='p1'>5</p>
        <p>6</p>
    <script src="05.js"></script>
    </body>
    <script>
        onload = function () {
    
        each(get('dv1'), function () {
            this.style.backgroundColor = "red";
        });
        each(get('p1'), function () {
            this.style.backgroundColor = "green";
        });
    };
    </script>
    

    chrome下效果:

    ie8下效果:

    关于getElementsByClassName()的兼容问题就讨论到这里,在下一篇,JavaScript高级框架设计(三),我会开始介绍push的兼容问题和 组合选择器.

    最后特别感谢恩师蒋坤老师(jk)对我的知识学习和人生引导的极大帮助,非常感谢他.

  • 相关阅读:
    Python之路【第十六篇】:冒泡排序、斐波那契数列、用户登陆程序
    Python之路【第十五篇】:文件操作
    Python之路【第十四篇】:Python的内置函数
    Python之路【第十三篇】:isinstance()函数与lambda表达式及函数参数的传递方式
    Python之路【第十二篇】:函数
    Python之路【第十一篇】:三目运算、不同数据类型的存储方式及深浅拷贝(copy模块)、列表推导式
    Python之路【第十篇】:索引与切片的不同
    Python之路【第九篇】:字符串与字节的相互转换以及2.7与3输出方式的不同
    Python之路【第八篇】:enumerate、range与xrange
    scala学习之类和对象
  • 原文地址:https://www.cnblogs.com/likeFlyingFish/p/5698695.html
Copyright © 2011-2022 走看看