sizzle分析記錄:分解流程

來源:互聯網
上載者:User

標籤:style   blog   http   color   strong   os   

<form>  <label>Name:</label>  <input name="name" />  <fieldset>      <label>Newsletter:</label>      <div name="newsletter" /><p>1<p</div>      <div name="letter" /><p name=‘aaron‘>2<p></div>      <div name="tter" /><p>3<p</div> </fieldset></form>

js

$("form div > p[name=aaron]")

解析的流程:

編譯器:分5個步驟

涉及: TAG元素 關係選取器 屬性選取器

1:通過tokenize詞法分析器分組

 

2:遍曆tokens,從右邊往左邊開始篩選,最快定位到目標元素合集

//先看看有沒有搜尋器find,搜尋器就是瀏覽器一些原生的取DOM介面,簡單的表述就是以下對象了            // Expr.find = {            // ‘ID‘    : context.getElementById,            // ‘CLASS‘ : context.getElementsByClassName,            // ‘TAG‘   : context.getElementsByTagName            //        }

操作如下

Expr.find["TAG"] = support.getElementsByTagName ?    function( tag, context ) {        if ( typeof context.getElementsByTagName !== strundefined ) {            return context.getElementsByTagName( tag );        }    } :

那麼第一篩選找到的定位元素,就形成了一個 seed種子合集,那麼餘下的所有的操作都是圍繞這個種子合集處理

因為節點總是存在各種關係的,所以不管是通過這個最靠近的目標的元素,往上還是往下 都是可以處理的

 

3:重組選取器,開始執行繼續分解"form div > [name=aaron]"

因為種子合已經抽出了,所以選取器就需要重新排列

"form div > [name=aaron]"

踢掉了P元素,已經被抽離了

 

4 : 產生編譯處理器

這裡為什麼要這麼複雜,因為產生了編譯閉包可以緩衝起來,通過這種機制,增加了重複選取器的效率

在matcherFromTokens方法中通過分解tokens產生對應的處理器

例如:form div [name=aaron]

在分解過程中分2大塊

A:關係選取器的處理  > + ~ 空

B: ATTR CHILD CLASS ID PSEUDO TAG的處理

用matchers保留組合關係

1:分解第一個TAG:form 儲存處理器到matchers.push( Expr.filter[“TAG”]) ;

2:分解第二個“空”的關係選取器,此時

 A:用elementMatcher把之前的matchers壓入到這個匹配裡面,產生一個遍曆方法的處理

function elementMatcher( matchers ) {    return matchers.length > 1 ?        function( elem, context, xml ) {            var i = matchers.length;            while ( i-- ) {                if ( !matchers[i]( elem, context, xml ) ) {                    return false;                }            }            return true;        } :        matchers[0];}

B:用addCombinator再次封裝,產生一個位置關係的尋找關係

function addCombinator( matcher, combinator, base ) {    var dir = combinator.dir,        checkNonElements = base && dir === "parentNode",        doneName = done++;    return         // Check against all ancestor/preceding elements        // 檢查所有祖先/元素        function( elem, context, xml ) {            var oldCache, outerCache,                newCache = [ dirruns, doneName ];                while ( (elem = elem[ dir ]) ) {                    if ( elem.nodeType === 1 || checkNonElements ) {                        outerCache = elem[ expando ] || (elem[ expando ] = {});                        if ( (oldCache = outerCache[ dir ]) &&                            oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {                            // Assign to newCache so results back-propagate to previous elements                            return (newCache[ 2 ] = oldCache[ 2 ]);                        } else {                            // Reuse newcache so results back-propagate to previous elements                            outerCache[ dir ] = newCache;                            // A match means we‘re done; a fail means we have to keep checking                            if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {                                return true;                            }                        }                    }                }        };}

 

所以此時的matchers的關係是一個層級的包含結構,然後依次這樣遞迴

這個地方相當繞!!!!

產生的最後

cached = matcherFromTokens( match[i] );

變成了一個超大的嵌套閉包

 

5: 通過matcherFromGroupMatchers這個函數來產生最終的匹配器

var bySet = setMatchers.length > 0,        byElement = elementMatchers.length > 0,        superMatcher = function(seed, context, xml, results, outermost) {            //分解這個匹配處理器        }    return superMatcher

通過matcherFromGroupMatchers的處理最直接的就是能看出,elementMatchers, setMatchers 2個結果不需要再返回出去,直接形成curry的方法,在內部就合并參數

外面就直接調用了,這樣

var compileFunc = compiled || compile( selector, match );compileFunc(    seed,    context,    !documentIsHTML,    results,    outermost);

compileFunc 一直是持有elementMatchers, setMatchers 的引用的,這個設計的手法還是值得借鑒的

 

執行期:

至此之前的5個步驟都是編譯成函數處理器的過程,然後就是開始執行了

粗的原理就是把直接分解出來的seed種子合集丟到這個處理器中,然後處理器就會根據各種關係進行分解匹配

從而得到結果集

superMatcher:

while ( (matcher = elementMatchers[j++]) ) {    if ( matcher( elem, context, xml ) ) {        results.push( elem );        break;    }}

抽出第一個seed元素,p

然後把p丟到atrr是過濾篩選器中去匹配下,看看是否能找到對應的這個屬性

當然還是繼續從右往左邊匹配過濾了

一次是【name=aaron】 => div => from

matchers[i] => Expr.filter.ATTR =>

p.getAttribute(‘name=aaron’) => 得到結果

function elementMatcher( matchers ) {    return matchers.length > 1 ?        function( elem, context, xml ) {            var i = matchers.length;            while ( i-- ) {                if ( !matchers[i]( elem, context, xml ) ) {                    return false;                }            }            return true;        } :        matchers[0];}

如果匹配失敗,自然就退出了  return false ,就不需要在往前找了 ,然後再次遞迴seed

如果成功,就需要再深入的匹配了

因為是從右至左逐個匹配,所以往前走就會遇到關係選取器的問題,

那麼jQuery把四種關係 > + ~ 空的處理給抽出一個具體的方法就是addCombinator

1 "form div > p[name=aaron]"
2 seed => p
3 篩選[name=aaron]
4 > => addCombinator方法 找到對應關係映射的父節點elem
5 elem中去匹配div 遞迴elementMatcher方法
6 “空” =>  addCombinator方法找到祖先父節點elem
7 elem中去找form為止
 
可見這個尋找是及其複雜繁瑣的

總結:

sizzle對選取器的大概是思路:

分解所有的選取器為最小單元,從右往左邊開始挑出一個瀏覽器的API能快速定位的元素TAG,ID,CLASS節點,這樣就能確定最終的元素跟這個元素是有關係的

然後把剩餘的選取器單元開始產生一個匹配器,主要是用來做篩選,最後根據關係分組

如果就依次匹配往上尋找,通過關係處理器定位上一個節點的元素,通過普通匹配器去確定是否為可選的內容

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.