Zeptoソースの不思議な$を読む
12735 ワード
前の3章の敷物を経て、この編はやっと戯肉に書いた.
Zeptoソースシリーズを読んでgithubに載せました.star:reading-zeptoを歓迎します.
ソースバージョン
本明細書で読むソースコードはzepto1.2.0
zeptoのcssセレクタ
ソースコード
以上は
この正則は実際には一致
なぜこんなに面倒なのか、直接
では、コードの逐行分析を開始します.
パラメータelementが検索を開始した要素 selectorセレクタ 変数#ヘンスウ# である可能性がある element.getElementById
まず、
次に、セレクタが単一セレクタであり、
戻り値は
不正なelementの排除
究極の三元表現
ここでは,取得した集合を
zepto.Z関数
第1編コード構造から、実装
zepto.isZ
$の実装zepto.init関数
$の実装
実際に
zepto.init
この
$の使い方
非参照呼び出し
直接
この分岐には、また3つのサブ分岐があります.ちょっと見てみましょう.
1番目の判定条件は、
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、 、
と組み合わせた単語であり、これは実際には単一id
とclass
の命名規則である.return
から分かるように、qsa
は、実際には、状況に応じてそれぞれ原生のgetElementById
、getElementsByClassName
、getElementsByTagName
およびquerySelectorAll
を呼び出す方法である.なぜこんなに面倒なのか、直接
querySelectorAll
メソッドを呼び出さないのか.これは性能の観点からです.ここには簡単なテストがあります.このテストでは、ページに1つの要素しかありません.複雑な場合、差がより明らかになります.では、コードの逐行分析を開始します.
パラメータ
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
等の形式ではない.first .secend
これは(element.getElementById && isSimple && maybeID)
を採用する条件である.まず、
element.getElementById
がelement
の方法を持っていることを確認します.getElementById
の方法はgetElementById
にあります.Chromeなどのブラウザではdocument
はelement
の方法を持っていない可能性があります.具体的には、この文章を参照してください.各ブラウザ対document.getElementByIdなどの方法の実装差異解析次に、セレクタが単一セレクタであり、
geElementById
セレクタであることを確認します.戻り値は
id
で、要素が検索できる場合は要素を配列で返します.そうでない場合は空の配列を返します.不正なelementの排除
((found = element.getElementById(nameOnly)) ? [found] : [])
. element.nodeType !== 1 && element.nodeType !== 9 && element.nodeType !== 11
は1
に対応し、Node.ELEMENT_NODE
は9
に対応し、Node.DOCUMENT_NODE
は11
に対応し、以上の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
関数は、isZ
がobject
のインスタンスであるかどうかを判断するために使用され、これは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()
オブジェクトが返されます.Z
がselector
の場合String
がselector
の場合、対応するコードは分岐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
の と するdom
がselector
に され、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
のように、 されたtest
はname
である.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
が な であるかどうかを します. えば、name
はtr
で まれ、その の は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
に、すべての は に の に され、 を し、 を します.
: の