Zeptoソースの不思議な$を読む


前の3章の敷物を経て、この編はやっと戯肉に書いた.zeptoを使う時、きっとこの不思議な$記号から離れられないに違いない.この文章はzeptoがどのように$を実現したのかを見るだろう.
Zeptoソースシリーズを読んでgithubに載せました.star:reading-zeptoを歓迎します.
ソースバージョン
本明細書で読むソースコードはzepto1.2.0
zeptoのcssセレクタzepto.qsa多くの場合、私たちは$でDOMオブジェクトを取得していることを知っています.これはzepto.qsaと大きな関係があります.
ソースコード
zepto.qsa = function(element, selector) {
        var found,  //      DOM
            maybeID = selector[0] == '#',  //    ID
            maybeClass = !maybeID && selector[0] == '.', //    class
            nameOnly = maybeID || maybeClass ? selector.slice(1) : selector,  //  id class       
            isSimple = simpleSelectorRE.test(nameOnly)  //         
        return (element.getElementById && isSimple && maybeID) ? 
            ((found = element.getElementById(nameOnly)) ? [found] : []) :
            (element.nodeType !== 1 && element.nodeType !== 9 && element.nodeType !== 11) ? [] :
            slice.call(
                isSimple && !maybeID && element.getElementsByClassName ? 
                maybeClass ? element.getElementsByClassName(nameOnly) : 
                element.getElementsByTagName(selector) : 
                element.querySelectorAll(selector) 
            )
    }

以上はqsaのすべてのコードで、中には正則表現simpleSelectorREがあり、まずこの正則を消化します.
simpleSelectorRE = /^[\w-]*$/,

この正則は実際には一致a-z、A-Z、0-9、 、 と組み合わせた単語であり、これは実際には単一idclassの命名規則である.returnから分かるように、qsaは、実際には、状況に応じてそれぞれ原生のgetElementByIdgetElementsByClassNamegetElementsByTagNameおよびquerySelectorAllを呼び出す方法である.
なぜこんなに面倒なのか、直接querySelectorAllメソッドを呼び出さないのか.これは性能の観点からです.ここには簡単なテストがあります.このテストでは、ページに1つの要素しかありません.複雑な場合、差がより明らかになります.
では、コードの逐行分析を開始します.
パラメータ
  • elementが検索を開始した要素
  • selectorセレクタ
  • 変数#ヘンスウ#
  • found:見つかった要素
  • maybeID = selector[0] == '#':セレクタの最初の文字が#であるか否かを判断し、# であればidセレクタ
  • である可能性がある
  • maybeClass = !maybeID && selector[0] == '.'セレクタではなく、セレクタの最初の文字がidであれば、.セレクタ
  • classで、nameOnly = maybeID || maybeClass ? selector.slice(1) : selectorセレクタまたはidセレクタの場合、最初の文字を削除
  • classが単一セレクタであるかどうか、すなわちisSimple = simpleSelectorRE.test(nameOnly)の形式であり、.single等の形式ではない
  • element.getElementById .first .secendこれは(element.getElementById && isSimple && maybeID)を採用する条件である.
    まず、element.getElementByIdelementの方法を持っていることを確認します.getElementByIdの方法はgetElementByIdにあります.Chromeなどのブラウザではdocumentelementの方法を持っていない可能性があります.具体的には、この文章を参照してください.各ブラウザ対document.getElementByIdなどの方法の実装差異解析
    次に、セレクタが単一セレクタであり、geElementByIdセレクタであることを確認します.
    戻り値はidで、要素が検索できる場合は要素を配列で返します.そうでない場合は空の配列を返します.
    不正なelementの排除((found = element.getElementById(nameOnly)) ? [found] : []) . element.nodeType !== 1 && element.nodeType !== 9 && element.nodeType !== 111に対応し、Node.ELEMENT_NODE9に対応し、Node.DOCUMENT_NODE11に対応し、以上の3つのタイプでなければNode.DOCUMENT_FRAGMENT_NODEに直接戻ります.
    究極の三元表現
    slice.call(
      isSimple && !maybeID && element.getElementsByClassName ?  //            id       getElementsByClassName  ,            
      maybeClass ? element.getElementsByClassName(nameOnly) :   //    class   ,   getElementsByClassName
      element.getElementsByTagName(selector) :  //     getElementsByTagName  
      element.querySelectorAll(selector)   //        ,  querySelectorAll
    )

    ここでは,取得した集合を[]で処理し,取得したDOM集合が配列の手法を直接利用できるようにした.
    zepto.Z関数
    第1編コード構造から、実装slice.call関数の核心は$であり、zepto.initは最終的にzepto.initの結果を返すことが分かった.では、まずzepto.Zを見てみましょう.
    zepto.Z = function(dom, selector) {
      return new Z(dom, selector)
    }
    zepto.Zのコードは簡単で、zepto.Z関数の例を返します.では次にZ関数を見てみましょう.
    function Z(dom, selector) {
      var i, len = dom ? dom.length : 0
      for (i = 0; i < len; i++) this[i] = dom[i]
      this.length = len
      this.selector = selector || ''
    }
    Z関数は、Z配列をクラス配列の形式に変換し、対応するdom属性とlength属性を設定することも簡単です.
    zepto.isZ
    zepto.isZ = function(object) {
      return object instanceof zepto.Z
    }
    selector関数を見たからには、ついでにZも一緒に見ましょう.isZ関数は、isZobjectのインスタンスであるかどうかを判断するために使用され、これはZで使用される.
    $の実装zepto.init関数
    $の実装
    $ = function(selector, context) {
      return zepto.init(selector, context)
    }

    実際にinitが呼び出されたのは$という内部メソッドであることがわかります.
    zepto.init
    zepto.init = function(selector, context) {
      var dom  // dom   
      if (!selector) return zepto.Z() //   1
      else if (typeof selector == 'string') { //   2
        selector = selector.trim()
        if (selector[0] == '

    このzepto.initメソッドのコード量は多くありませんが、大量のinitがあります.はっきり言ってほしいです.
    $の使い方
    $(selector, [context])   ⇒ collection  //   1
    $()   ⇒ same collection //   2
    $()   ⇒ collection //   3
    $(htmlString)   ⇒ collection //   4
    $(htmlString, attributes)   ⇒ collection v1.0+ //   5
    Zepto(function($){ ... })   //   6

    非参照呼び出し
    直接if elseを呼び出すと、ブランチ1の場合:$()に対応し、空のif (!selector) return zepto.Z()オブジェクトが返されます.Zselectorの場合Stringselectorの場合、対応するコードは分岐2にあり、対応する用法は用法1、用法4、用法5である.
    この分岐には、また3つのサブ分岐があります.ちょっと見てみましょう.
    1番目の判定条件は、stringの1番目の文字がselector[0] == ' 。selectorであり、htmlタグである.<の は、 がラベルであるか かを するために いられるfragmentREである. は にもあまり しくないので、ここはもう しません.
    が たされた 、fragmentRE = /^\s*]*>/というコードが されます.dom = zepto.fragment(selector, RegExp.$1, context), selector = nullは、zepto.fragmentを してdomセットを します.この は しますが、ここでは しません.ここでの は 4と 5です.
    の を たさない は、htmlString(コンテキストが するか か)を する. する 、ルックアップcontext !== undefined セレクタはcontextのすべてのサブ :selectorである.この は 1に している
    そうでなければ、 $(context).find(selector)メソッドが び され、zepto.qsaの にあるすべてのdocument:selectorが される.ここでの は 1です.dom = zepto.qsa(document, selector)selector
    するコードは 3で、 する い は 6です.
    このブランチは で、ページのロードが したら、コールバック を します:Function$(document).ready(selector)を ったことがある は、この い をよく っているはずです.zeptoです. はこの を いています$(function() {})selector
    するコードはブランチ4で、 する い は 2です.
    パラメータが にZオブジェクト(Z)であれば、 もする はなく、 のオブジェクトに ればよい.zepto.isZ(selector)その の
    の (selector)、 を らにします(isArray(selector))
    オブジェクトの (dom = compact(selector))、オブジェクトを (isObject(selector))にラップします.
    の2つの は 3に し,domオブジェクトまたはdom をdom = [selector]オブジェクトに する.
    ラベル(z)の 、ブランチ1と じコードが されます.ここで するのは ですでにしたので、どうしてもう ますか? もよくわかりませんが、はっきりしたことがあれば に ってください.
    の とfragmentRE.test(selector)リセットを て、やっとselector を び すことができます:z,zepto.Z(dom, selector)の に、 されたinitの と するdomselector に され、Zオブジェクトに ります.
    zepto.fragment
    zepto.fragment = function(html, name, properties) {
      var dom, nodes, container
      if (singleTagRE.test(html)) dom = $(document.createElement(RegExp.$1))
      if (!dom) {
        if (html.replace) html = html.replace(tagExpanderRE, "$2>")
        if (name === undefined) name = fragmentRE.test(html) && RegExp.$1
        if (!(name in containers)) name = '*'
        container = containers[name]
        container.innerHTML = '' + html
        dom = $.each(slice.call(container.childNodes), function() {
          container.removeChild(this)
        })
      }
      if (isPlainObject(properties)) {
        nodes = $(dom)
        $.each(properties, function(key, value) {
          if (methodAttributes.indexOf(key) > -1) nodes[key](value)
          else nodes.attr(key, value)
            })
      }
      return dom
    }
    Zはhtml をdom に する を たす.
    まず、ラベルの fragment( えばsingleTagRE.test(html))であるか かを し、もしそうであれば、このラベル を いてdomオブジェクトを し、 の を わなくてもよい.dom = $(document.createElement(RegExp.$1)) . singleTagRE = /^(?:|)$/がまだ されていない は、 の に います.
    if (html.replace) html = html.replace(tagExpanderRE, "$2>")

    この はdomを し、 えばhtmlに する. は
    if (name === undefined) name = fragmentRE.test(html) && RegExp.$1

    タグ が されていない は、タグ を します. tagExpanderRE = /]*)\/>/igのように、 されたtestnameである.
    if (!(name in containers)) name = '*'
        container = containers[name]
        container.innerHTML = '' + html
        dom = $.each(slice.call(container.childNodes), function() {
          container.removeChild(this)
        })
      }
    // containers       ,  
    table = document.createElement('table'),
            tableRow = document.createElement('tr'),
    containers = {
      'tr': document.createElement('tbody'),
      'tbody': table,
      'thead': table,
      'tfoot': table,
      'td': tableRow,
      'th': tableRow,
      '*': document.createElement('div')
    }
    divが な であるかどうかを します. えば、nametrで まれ、その の はtbodyで まれます.ラップ のdivは、 する があるchildNodesである.
    if (isPlainObject(properties)) {
        nodes = $(dom)
        $.each(properties, function(key, value) {
          if (methodAttributes.indexOf(key) > -1) nodes[key](value)
          else nodes.attr(key, value)
            })
      }
    // methodAttributes        ,    
    methodAttributes = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset']

    アトリビュート が なオブジェクトの 、 にアトリビュートを します.
    に じて されたプロパティがzeptoによって されている 、zeptoに するメソッドが び され、そうでない 、zeptoのdomメソッド プロパティが に び されます.
    attrを します
    シリーズ
  • Zeptoソースコードのコード を む
  • Zeptoソースの メソッドを む
  • Zeptoソースのツール を む

  • リファレンス
  • ブラウザ document.getElementByIdなどの の
  • Node.nodeType

  • に、すべての は に の に され、 を し、 を します.
    : の