Zeptoソースコード分析3-qsa実現とツール関数設計
7765 ワード
第一編の末尾の内容を受けて、この部分はzepoメインモジュールに入り、その設計思想と実現技術を分析します.
Zeptoのセレクタ
Zeptoのいくつかのツール関数の設計
Zeptoの配列は、オブジェクト関連のツール関数と比較してUnderscore.jsに似ています.いくつかの技術的な実現を重点的に列挙します.タイプの関連ツール関数の例: は、要素とセレクタとの整合性を判定する関数 を有する.は、現在のブラウザにある要素のデフォルト
本節の終わりに、Zeptoを広げる方法を簡単に紹介します.メインモジュールZeptoの外に、デフォルトでコンパイルされていないモジュールSelectorは、拡張元
Zeptoのセレクタ
zepto.qsa()
//\ Line 262
zepto.qsa = function(element, selector) {
};
まず最初のプロトタイプチェーン構造に直接関係しないツール関数qsa
から、Zeptoの設計思想を観察する. //\ Line 28
simpleSelectorRE = /^[\w-]*$/,
//\ Line 337
var found,
maybeID = selector[0] == "#",
maybeClass = !maybeID && selector[0] == ".",
nameOnly = maybeID || maybeClass ? selector.slice(1) : selector, // Ensure that a 1 char tag name still gets checked
isSimple = simpleSelectorRE.test(nameOnly);
関数の開始部分は、まずいくつかのBool値を定義し、id
またはclass
である可能性があるかどうかを推測するために使用している.この場合、もし両者のうちの一つである可能性があるならば、マーク部分を削除する(. or #
).そうでなければ、自身をnameOnly
と表記する.simpleSelectorRE
は、一度のマーク部分を剥離した可能性があるselectorが、一般文字列であることを満たすかどうかをテストするために使用され、そうでない場合は、複数の条件の組み合わせ(.class1.class2
)である可能性があり、直接に元のquerySelectorAll
方法で照会する. //\ Line 268
return element.getElementById && isSimple && maybeID // Safari DocumentFragment doesn't have getElementById
? (found = element.getElementById(nameOnly))
? [found]
: []
一連の判断を含むreturn
段階に入ると、268行には互換性の注釈が現れ、前方のmaybeClass
定義にはid
ではないと宣言されているので、ここではgetElementById
をサポートしない方法もそのまま元に戻すquerySelectorAll
方法がある.検索条件を満たしていれば、元のgetElementById`方法で照会し、配列方式の結果を返します. //\ Line 6
var undefined,
key,
$,
classList,
emptyArray = [],
concat = emptyArray.concat,
filter = emptyArray.filter,
slice = emptyArray.slice,
//\ Line 270
: element.nodeType !== 1 &&
element.nodeType !== 9 &&
element.nodeType !== 11
? []
: slice.call(
isSimple && !maybeID && element.getElementsByClassName // DocumentFragment doesn't have getElementsByClassName/TagName
? maybeClass
? element.getElementsByClassName(nameOnly) // If it's simple, it could be a class
: element.getElementsByTagName(selector) // Or a tag
: element.querySelectorAll(selector) // Or it's not simple, and we need to query all
);
nodeTypeを参照してルート探索要素タイプを判断したが、ここではid
と同じ降格ポリシーを採用し、空配列上の方法を呼び出してArray.prototype
上のslice
方法を呼び出して配列生成を完了し、全体Zeptoライブラリは実際に同じ考え方で原型チェーンを使ってZオブジェクト上の動作方法を与えた.Zeptoのいくつかのツール関数の設計
Zeptoの配列は、オブジェクト関連のツール関数と比較してUnderscore.jsに似ています.いくつかの技術的な実現を重点的に列挙します.
//\ Line 29
class2type = {},
toString = class2type.toString,
//\ Line 401
// Populate the class2type map
$.each(
"Boolean Number String Function Array Date RegExp Object Error".split(" "),
function(i, name) {
class2type["[object " + name + "]"] = name.toLowerCase();
}
);
//\ Line 65
function type(obj) {
return obj == null ? String(obj) :
class2type[toString.call(obj)] || "object"
}
ツール関数type
に==
演算子が現れ、ここでnull/undefined == null
の言語特性を利用し、String
のパッケージクラスを通してタイプ変換して、そのタイプの文字列表現が得られ、これらの2つのタイプでない場合はclass2type
のマッピング関係によって対応する文字列タイプ名に変換される. //\ Line 78
function likeArray(obj) {
var length = !!obj && 'length' in obj && obj.length,
type = $.type(obj)
return 'function' != type && !isWindow(obj) && (
'array' == type || length === 0 ||
(typeof length == 'number' && length > 0 && (length - 1) in obj)
)
}
ツール関数likeArray
は、実際には、length
のNumber型メンバー変数およびKey値がlength - 1
のメンバ変数が存在し、関数のオブジェクトではないZeptoが考えられる配列形態を与えている.このような定義は、ローズマリーモードを使用してもよく、初期化されていない配列項目がundefinedタイプの言語属性を適切に使用してもよい.matches
qsa()
関数と同様に、Zeptoはまた、ある要素が与えられたセレクタと一致するかどうかを判断するためのタイプマッチング関数zepto.matches()
を提供する.//\ Line 33
tempParent = document.createElement('div'),
//\ Line 51
zepto.matches = function(element, selector) {
//\ , False
if (!selector || !element || element.nodeType !== 1) return false;
//\ Element.prototype.matches() -
//\ https://dom.spec.whatwg.org/#dom-element-matches
var matchesSelector =
element.matches ||
element.webkitMatchesSelector ||
element.mozMatchesSelector ||
element.oMatchesSelector ||
element.matchesSelector;
if (matchesSelector) return matchesSelector.call(element, selector);
//\ matches API, qsa
//\ , qsa()
//\ , , qsa()
// fall back to performing a selector:
var match,
parent = element.parentNode,
temp = !parent;
if (temp) (parent = tempParent).appendChild(element);
match = ~zepto.qsa(parent, selector).indexOf(element);
//\
temp && tempParent.removeChild(element);
return match;
};
同様に、親レベルの容器を構成して、サブレベルの要素の性質の考え方を問い合わせるために、Zeptoソースコードには、例えば別のツール関数defaultDisplay
の実装において複数回現れる.display
値のdefaultDisplay()
関数を取得する.DOMにおける要素のデフォルトのスタイル値は、実際にユーザが変更する前に、ブラウザにノードタイプのデフォルト値を付与するので、クエリ要素のデフォルト値は、あるノードタイプのデフォルト値を照会することになる.//\ Line 8
elementDisplay = {}
//\ Line 109
function defaultDisplay(nodeName) {
var element, display;
//\ elementDisplay nodeName ,
if (!elementDisplay[nodeName]) {
//\ , body display
element = document.createElement(nodeName);
document.body.appendChild(element);
//\ IE getComputedStyle()
display = getComputedStyle(element, "").getPropertyValue("display");
//\ , display none block
//\ none display $.fn.show()
element.parentNode.removeChild(element);
display == "none" && (display = "block");
//\ elementDisplay
elementDisplay[nodeName] = display;
}
return elementDisplay[nodeName];
}
//\ Line 574
show: function() {
return this.each(function() {
this.style.display == "none" && (this.style.display = "");
//\ defaultDisplay() none block
if (getComputedStyle(this, "").getPropertyValue("display") == "none")
this.style.display = defaultDisplay(this.nodeName);
});
},
Zepto荷重拡張の方法本節の終わりに、Zeptoを広げる方法を簡単に紹介します.メインモジュールZeptoの外に、デフォルトでコンパイルされていないモジュールSelectorは、拡張元
qsa()
関数の実装を含み、モジュールコードsrc/selector.js
に入る.その構造は以下の通りである.(function($) {
var zepto = $.zepto,
oldQsa = zepto.qsa,
oldMatches = zepto.matches;
zepto.qsa = function(node, selector) {
//\ zepto.qsa
};
zepto.matches = function(node, selector) {
//\ zepto.matches
};
})(Zepto);
実際のコンパイルでは、Selectorをコアモジュールの後にコンパイルするだけで、元のqsa
関数と対応するmatches
関数を置き換えることができますので、この考え方に基づくZepto外注モジュールは非常に簡単です.コアモジュール論理を分析する際に、この方法で関数を書き換えたり、ビジネスニーズに基づいて新しいデータ構造を配置してみたり、Zeptoを利用してDOMの添削調査を実現します.