jQuery.API源码深入剖析以及应用实现(3) - 选择器篇(上)

还漏了一个框题,jQuery的冲突机制解决方法jQuery.noConflict()以及jQuery.noConflict(extreme),这里先分析一下:

jQuery.noConflict():运行这个函数将变量$的控制权让渡给第一个实现它的那个库。

jQuery.noConflict(extreme):将$和jQuery的控制权都交还给原来的库。

比如在prototype框架中的$会和jQuery框架中的$产生命名冲突,这里就是为了解决这种问题。

现在先看下noConflict方法的具体实现:

noConflict: function( deep ) {
    window.$ = _$; 

    if ( deep )
        window.jQuery = _jQuery; 

    return jQuery;
}

其中_$,_jQuery是在jQquery源码的开始几行定义的:

(function(){ 

var 
       // 
    _jQuery = window.jQuery,
    _$ = window.$,
       // 
})();

他们都是为了防止$被覆盖而将window.jQuery,window.$放在临时变量中保存起来。

当deep为空的时候,“_$”覆盖“window.$”,“$”的常规功能失效,但jQuery还可以继续使用。当有新的库中重新定义“$”的时候,“jQuery”继续为jQquery的常规功能,而“$”就不是jQuery中的了,它是属于新的库的常规功能;

当deep不为空的时候,它将“_jQuery”覆盖“window.jQuery”,这样导致可能jQuery插件失效;另外方法返回的jQuery,实际上没有被覆盖。通过它完全可以移到新的一个命名空间,如dom.query = jQuery.noConflict(true); dom.query("div p").hide();

前言

这篇文章将介绍jQuery选择器的原理,主要内容包括:

分析

一、基本

1. 【#id】和【element】

 在第一篇中曾经提到核心函数的概念,形如$("#result")【jQuery(expression,[context])】表达式,归根调用【jQuery(elements)】,因此将调用:

if ( selector.nodeType ) {
    this[0] = selector;
    this.length = 1;
    this.context = selector;
    return this;
}

 

2. 【.class】

第一篇中曾经提到核心函数的概念,形如 $(".container") 【jQuery(expression,[context])】表达式字符串的实现。

3. 【*】

通过第一篇的结论,将代码进行到:

Sizzle.find = function(expr, context, isXML){
    var set, match; 

    if ( !expr ) {
        return [];
    } 

    for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
        var type = Expr.order[i], match;
        if ( (match = Expr.match[ type ].exec( expr )) ) {
            var left = RegExp.leftContext; 

            if ( left.substr( left.length - 1 ) !== """" ) {
                match[1] = (match[1] || "").replace(/""/g, "");
                set = Expr.find[ type ]( match, context, isXML ); 
                if ( set != null ) {
                    expr = expr.replace( Expr.match[ type ], "" );
                    break;
                }
            }
        }
    } 

    if ( !set ) {
        set = context.getElementsByTagName("*");
    } 

    return {set: set, expr: expr};
};

当expr为“*”的时候,根据Expr.match[ type ].exec( expr )为true时,type为TAG,因此将执行:

if ( left.substr( left.length - 1 ) !== """" ) {
    match[1] = (match[1] || "").replace(/""/g, "");
    set = Expr.find[ type ]( match, context, isXML ); 
    if ( set != null ) {
        expr = expr.replace( Expr.match[ type ], "" );
        break;
    }
}

继续查看Expr.find[ type ]的方法,具体实现如下:

var Expr = Sizzle.selectors = {
  //

  find: {
    ID: function(match, context, isXML){
        if ( typeof context.getElementById !== "undefined" && !isXML ) {
            var m = context.getElementById(match[1]);
            return m ? [m] : [];
        }
    },
    NAME: function(match, context, isXML){
        if ( typeof context.getElementsByName !== "undefined" && !isXML ) {
            return context.getElementsByName(match[1]);
        }
    },
    TAG: function(match, context){
        return context.getElementsByTagName(match[1]);
    }
}

所以将调用context.getElementsByTagName("*");返回context中所有的DOM元素。

4. 【selector1,selector2,selectorN】

参考第一篇中的内容,当表达式包含“,”符号的时候,最后也是返回一个jQuery对象。

二、层级

1. 【ancestor descendant】

在给定的祖先元素下匹配所有的后代元素。

HTML代码 jQuery代码 结果
<form>
  <label>Name:</label>
  <input name="name" />
  <fieldset>
      <label>Newsletter:</label>
      <input name="newsletter" />
 </fieldset>
</form>
<input name="none" />  
$("form input") [ <input name="name" />, <input name="newsletter" /> ]

首先“ancestor descendant” 作为一个表达式字符串,根据第一篇中的内容,它将执行:

// 处理 形如 $("div .container")的表达式字符串
else
  return jQuery( context ).find( selector );

接着查看jQuery对象的find方法:

find: function( selector ) {
    // 当表达式不包含“,”符号时候
    if ( this.length === 1 && !/,/.test(selector) ) {
        var ret = this.pushStack( [], "find", selector );
        ret.length = 0;
        jQuery.find( selector, this[0], ret );
        return ret;
    } 
    // 当表达式包含“,”符号时候
    else {
        var elems = jQuery.map(this, function(elem){
            return jQuery.find( selector, elem );
        });

        return this.pushStack( /[^+>] [^+>]/.test( selector ) ?
            jQuery.unique( elems ) :
            elems, "find", selector );
    }
}

由于jQuery.find = Sizzle; 因此查看Sizzle对象的具体实现:

Code

其中set = Sizzle.filter( ret.expr, ret.set );调用Sizzle.filter方法:

Sizzle.filter = function(expr, set, inplace, not){
    var old = expr, result = [], curLoop = set, match, anyFound;

    while ( expr && set.length ) {
        for ( var type in Expr.filter ) {
            if ( (match = Expr.match[ type ].exec( expr )) != null ) {
                var filter = Expr.filter[ type ], found, item;
                anyFound = false;

                if ( curLoop == result ) {
                    result = [];
                }

                if ( Expr.preFilter[ type ] ) {
                    match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not );

                    if ( !match ) {
                        anyFound = found = true;
                    } else if ( match === true ) {
                        continue;
                    }
                }

                if ( match ) {
                    for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
                        if ( item ) {
                            found = filter( item, match, i, curLoop );
                            var pass = not ^ !!found;

                            if ( inplace && found != null ) {
                                if ( pass ) {
                                    anyFound = true;
                                } else {
                                    curLoop[i] = false;
                                }
                            } else if ( pass ) {//$("form input")从这里进
                                result.push( item );
                                anyFound 
= true;
                            }
                        }
                    }
                }

                if ( found !== undefined ) {
                    if ( !inplace ) {
                        curLoop = result;
                    }

                    expr = expr.replace( Expr.match[ type ], "" );

                    if ( !anyFound ) {
                        return [];
                    }

                    break;
                }
            }
        }

        expr = expr.replace(/"s*,"s*/, "");

        // Improper expression
        if ( expr == old ) {
            if ( anyFound == null ) {
                throw "Syntax error, unrecognized expression: " + expr;
            } else {
                break;
            }
        }

        old = expr;
    }

    return curLoop;
};

最关键是在加粗字代码,result.push( item ); anyFound = true; 和 curLoop = result;将匹配的元素加入result中,然后赋值于curLoop。

而方法的最后返回的是curLoop。所匹配的元素就是最后所需要的jQuery对象。

2. 【parent > child】

在给定的父元素下匹配所有的子元素。

HTML代码 jQuery代码 结果
<form>
  <label>Name:</label>
  <input name="name" />
  <fieldset>
      <label>Newsletter:</label>
      <input name="newsletter" />
 </fieldset>
</form>
<input name="none" />
$("form > input") [ <input name="name" /> ]

我们只要查看一下它的核心代码:

relative: {
    //
    ">": function(checkSet, part, isXML){
        // 当part为单词字符时,如$("form > input"),part为“form”
        if ( typeof part === "string" && !/"W/.test(part) ) { 
            part = isXML ? part : part.toUpperCase(); 

            for ( var i = 0, l = checkSet.length; i < l; i++ ) {
                var elem = checkSet[i];
                if ( elem ) {
                    // 得到elem的父节点
                    var parent = elem.parentNode;
                    // 如果父节点名称为part值时,在checkSet[i]上赋值父节点,否则赋值false
                    checkSet[i] = parent.nodeName === part ? parent : false;
                }
            }
        // 当part为非单词字符时,如$(".blue > input"),part为“.blue”
        } else {
            for ( var i = 0, l = checkSet.length; i < l; i++ ) {
                var elem = checkSet[i];
                if ( elem ) {
                    checkSet[i] = typeof part === "string" ?
                        elem.parentNode :
                        elem.parentNode === part;
                }
            } 

            if ( typeof part === "string" ) {
                Sizzle.filter( part, checkSet, true );
            }
        }
    },
}

从这里我们可以得到checkSet的值集合。

2. 【prev + next】

匹配所有紧接在 prev 元素后的 next 元素。next (Selector) :一个有效选择器并且紧接着第一个选择器。

例子

HTML代码 jQuery代码 结果
<form>
  <label>Name:</label>
  <input name="name" />
  <fieldset>
      <label>Newsletter:</label>
      <input name="newsletter" />
 </fieldset>
</form>
<input name="none" /> 
$("label + input") [ <input name="name" />, <input name="newsletter" /> ]

只要查看一下它的核心代码:

relative: {
    // 
    "+": function(checkSet, part){ 
        for ( var i = 0, l = checkSet.length; i < l; i++ ) {
            var elem = checkSet[i];
            if ( elem ) {
                // 得到elem的前一个节点
                var cur = elem.previousSibling;
                // 当cur的节点类型不为元素节点的时候,继续得到cur的前一个节点,否则循环结束
                while ( cur && cur.nodeType !== 1 ) {
                    cur = cur.previousSibling;
                }
                checkSet[i] = typeof part === "string" ?
                    cur || false :
                    cur === part;
            }
        } 

        if ( typeof part === "string" ) {
            Sizzle.filter( part, checkSet, true );
        }
    },
    // 
}

从这里我们可以得到checkSet的值集合。

3. 【prev ~ next】

匹配 prev 元素之后的所有 siblings 元素。

例子

HTML代码 jQuery代码 结果
<form>
  <label>Name:</label>
  <input name="name" />
  <fieldset>
      <label>Newsletter:</label>
      <input name="newsletter" />
 </fieldset>
</form>
<input name="none" /> 
<input name="none2" />
$("form ~ input") [ <input name="none" />, <input name="none2" />]

只要查看一下它的核心代码:

relative: {
    // 
    "~": function(checkSet, part, isXML){ 
        var doneName = "done" + (done++), checkFn = dirCheck; 

        if ( typeof part === "string" && !part.match(/"W/) ) {
            var nodeCheck = part = isXML ? part : part.toUpperCase();
            checkFn = dirNodeCheck;
        } 

        checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
    }
}

其中checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML); 调用的是dirCheck方法,它的具体实现为:

function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
    for ( var i = 0, l = checkSet.length; i < l; i++ ) {
        var elem = checkSet[i];
        if ( elem ) {
            elem = elem[dir];
            var match = false; 

            while ( elem && elem.nodeType ) {
                if ( elem[doneName] ) {
                    match = checkSet[ elem[doneName] ];
                    break;
                } 

                if ( elem.nodeType === 1 ) {
                    if ( !isXML )
                        elem[doneName] = i; 

                    if ( typeof cur !== "string" ) {
                        if ( elem === cur ) {
                            match = true;
                            break;
                        } 

                    } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
                        match = elem;
                        break;
                    }
                } 
              // 由于这里dir为previousSibling,所以这里利用循环不断得到elem的前一个节点,并且赋值checkSet数组
                elem = elem[dir];
            } 

            checkSet[i] = match;
        }
    }
}

从这里我们可以得到checkSet的值集合。


三、简单

1. 【:first】,【:last】,【:even】,【:odd】,【 :eq(index) 】,【 :gt(index) 】,【 :lt(index) 】和 【 :not(selector) 】

:first 匹配找到的第一个元素。

:last 匹配找到的最后一个元素。

:even 匹配所有索引值为偶数的元素,从 0 开始计数。

:odd 匹配所有索引值为奇数的元素,从 0 开始计数。

:eq(index) 匹配一个给定索引值的元素。

:gt(index) 匹配所有大于给定索引值的元素。

:lt(index) 匹配所有小于给定索引值的元素。

:not(selector) 去除所有与给定选择器匹配的元素。

例子

HTML代码 jQuery代码
<table>
  <tr><td>Header 1</td></tr>
  <tr><td>Value 1</td></tr>
  <tr><td>Value 2</td></tr>
</table>
$("tr:first"),$("tr:last"),$("tr:even"),$("tr:odd"),$("tr:eq(1)"),$("tr:gt(0)")

首先我们看下它的一个正则表达式:

POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:"(("d*)"))?(?=[^-]|$)/,

核心代码从Sizzle.filter开始:

让我们先看下var filter = Expr.filter[ type ],的Expr.filter的具体实现,核心代码为:

接着看下Expr.setFilters的具体实现:

setFilters: {
    first: function(elem, i){
        return i === 0;
    },
    last: function(elem, i, match, array){
        return i === array.length - 1;
    },
    even: function(elem, i){
        return i % 2 === 0;
    },
    odd: function(elem, i){
        return i % 2 === 1;
    },
    lt: function(elem, i, match){
        return i < match[3] - 0;
    },
    gt: function(elem, i, match){
        return i > match[3] - 0;
    },
    nth: function(elem, i, match){
        return match[3] - 0 == i;
    },
    eq: function(elem, i, match){
        return match[3] - 0 == i;
    }
}

噢,所有的标识 主要在这里判断elem元素在集合中的逻辑位置,并且返回一个布尔值。

接着 match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not );  Expr.prefilter的具体实现,主要核心代码为:

PSEUDO: function(match, curLoop, inplace, result, not){
            if ( match[1] === "not" ) {
              
                // 代码1
                if ( match[3].match(chunker).length > 1 ) {
                    match[3] = Sizzle(match[3], null, null, curLoop);
                } else {
                    var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
                    if ( !inplace ) {
                        result.push.apply( result, ret );
                    }
                    return false;
                }
            } else if ( Expr.match.POS.test( match[0] ) ) {
                return true;
            }
            return match;
        }

当match[1]匹配中包含为“not”时,即表达式字符串中包含:not时,发生“代码1”;否则,根据POS的正则表达式判断返回true。

最后将匹配的item元素入栈,即 result.push( item )。Sizzle.filter最后返回的是curLoop。所匹配的元素就是最后所需要的jQuery对象。

时间: 2024-09-21 13:19:17

jQuery.API源码深入剖析以及应用实现(3) - 选择器篇(上)的相关文章

jQuery.API源码深入剖析以及应用实现(4) - 选择器篇(下)

继续介绍选择器的其它原理,包括内容,可见性,属性,子元素,表单,表单对象属性等等原理. jQuery选择器的图示包括: 一.内容 1. [ :contains(text) ] 匹配包含给定文本的元素. 例子 HTML代码 jQuery代码 结果 <div>John Resig</div> <div>George Martin</div> <div>Malcom John Sinclair</div> <div>J. Ohn

【深入浅出jQuery】源码浅析2--奇技淫巧

最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐进增强)优雅的处理能力以及 Ajax 等方面周到而强大的定制功能无不令人惊叹. 另外,阅读源码让我接触到了大量底层的知识.对原生JS .框架设计.代码优化有了全新的认识,接下来将会写一系列关于 jQuery 解析的文章. 我在 github 上关于 jQuery 源码的全文注解,感兴趣的可以围观一下

Portal Starter 源码深入剖析(一)

Portal Starter 源码深入剖析(一)学ASP.net光看书看来是不行的,找一些经典的源代码来读读,对提升认识是很有帮助的.在Microsoft的网站上找到几个范例,选择Portal是因为这个范例最大,可作为一个简单的门户站.Portal的工作流程:1.读取网站设置文件PortalCfg.xml至context中缓存起来,这个过程由Global.asax中的Application_BeginRequest()事件来完成的.2.客户访问Portal站,执行Default.aspx,Def

如何Debug JAVA api源码

问题描述 现在下载下来API源码了写了一个测试类,想debug看下源码是怎么实现的.例如:importjava.util.HashMap;publicclassTestHashMap{/***@paramargs*/publicstaticvoidmain(String[]args){HashMap<String,String>map=newHashMap<String,String>();map.put("name","username")

【深入浅出jQuery】源码浅析--整体架构

最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐进增强)优雅的处理能力以及 Ajax 等方面周到而强大的定制功能无不令人惊叹. 另外,阅读源码让我接触到了大量底层的知识.对原生JS .框架设计.代码优化有了全新的认识,接下来将会写一系列关于 jQuery 解析的文章. 网上已经有很多解读 jQuery 源码的文章了,作为系列开篇的第一篇,思前想去

tomcat集群实现源码级别剖析

随着互联网快速发展,各种各样供外部访问的系统越来越多且访问量越来越大,以前Web容器可以包揽接收-逻辑处理-响应整个请求生命周期的工作,现在为了构建让更多用户访问更强大的系统,人们通过不断地业务解耦.架构解耦将web容器的逻辑处理抽离交由其他中间件处理,例如缓存中间件.消息队列中间件.数据存储中间件等等.Web容器负责的工作可能越来越少,但是它确实必不可少的部分,它负责接收用户请求并分别调用各个服务最后响应.可以说目前最受欢迎的web容器是用Java写的tomcat小猫,由于生产上的tomcat

jQuery源码分析-03构造jQuery对象-源码结构和核心函数_jquery

作者:nuysoft/高云 QQ:47214707 EMail:nuysoft@gmail.com 毕竟是边读边写,不对的地方请告诉我,多多交流共同进步.本章还未写完,完了会提交PDF. 前记: 想系统的好好写写,但是会先从感兴趣的部分开始. 近期有读者把PDF传到了百度文库上,首先感谢转载和传播,但是据为已有并设置了挺高的财富值才能下载就不好了,以后我整理好了会传到文库上.请体谅一下. 3. 构造jQuery对象 3.1 源码结构 先看看总体结构,再做分解: 复制代码 代码如下: (funct

mahout源码分析之DistributedLanczosSolver(七) 总结篇

Mahout版本:0.7,hadoop版本:1.0.4,jdk:1.7.0_25 64bit. 看svd算法官网上面使用的是亚马逊的云平台计算的,不过给出了svd算法的调用方式,当算出了eigenVectors后,应该怎么做呢?比如原始数据是600*60(600行,60列)的数据,计算得到的eigenVectors是24*60(其中的24是不大于rank的一个值),那么最后得到的结果应该是original_data乘以eigenVectors的转置这样就会得到一个600*24的矩阵,这样就达到了

请问哪位高手对Liferay Portal 的源码有研究?或者对在Liferay Portal 上进行Portlet开发有研究的,请指点一下学习路线。谢谢

问题描述 请问哪位高手对LiferayPortal的源码有研究?或者对在LiferayPortal上进行Portlet开发有研究的,请指点一下学习路线.谢谢 解决方案 解决方案二:以前想用来着,LIFERAY太大大难,作罢.解决方案三:在源码上开发了3个月,还是有点理不清,那东西有点难!不好说.