最近在做移动端web项目,用到了开源框架zepto,语法上和jquery并无明显区别,所以怀着好奇的心情看了一下其源代码,接下来先介绍其核心方法$()
一、$()的源码
// `$` will be the base `Zepto` object. When calling this
// function just call `$.zepto.init, which makes the implementation
// details of selecting nodes and creating Zepto collections
// patchable in plugins.
$ = function(selector, context){
return zepto.init(selector, context)
}
可以看到$()调用了zepto.init(selctor, context),下面进入zepto的init中
// `$.zepto.init` is Zepto's counterpart to jQuery's `$.fn.init` and
// takes a CSS selector and an optional context (and handles various
// special cases).
// This method can be overriden in plugins.
zepto.init = function(selector, context) {
var dom
// If nothing given, return an empty Zepto collection
if (!selector) return zepto.Z()
// Optimize for string selectors
else if (typeof selector == 'string') {
selector = selector.trim()
// If it's a html fragment, create nodes from it
// Note: In both Chrome 21 and Firefox 15, DOM error 12
// is thrown if the fragment doesn't begin with <
if (selector[0] == '<' && fragmentRE.test(selector))
dom = zepto.fragment(selector, RegExp.$1, context), selector = null
// If there's a context, create a collection on that context first, and select
// nodes from there
else if (context !== undefined) return $(context).find(selector)
// If it's a CSS selector, use it to select nodes.
else dom = zepto.qsa(document, selector)
}
// If a function is given, call it when the DOM is ready
else if (isFunction(selector)) return $(document).ready(selector)
// If a Zepto collection is given, just return it
else if (zepto.isZ(selector)) return selector
else {
// normalize array if an array of nodes is given
if (isArray(selector)) dom = compact(selector)
// Wrap DOM nodes.
else if (isObject(selector))
dom = [selector], selector = null
// If it's a html fragment, create nodes from it
else if (fragmentRE.test(selector))
dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null
// If there's a context, create a collection on that context first, and select
// nodes from there
else if (context !== undefined) return $(context).find(selector)
// And last but no least, if it's a CSS selector, use it to select nodes.
else dom = zepto.qsa(document, selector)
}
// create a new Zepto collection from the nodes found
return zepto.Z(dom, selector)
}
第一步,在init方法中,首先判断如果传入的selector为空,则返回一个空的zepto集合z()
// `$.zepto.Z` swaps out the prototype of the given `dom` array
// of nodes with `$.fn` and thus supplying all the Zepto functions
// to the array. Note that `__proto__` is not supported on Internet
// Explorer. This method can be overriden in plugins.
zepto.Z = function(dom, selector) {
dom = dom || []
dom.__proto__ = $.fn
dom.selector = selector || ''
return dom
}
在z方法中,待返回的dom的_proto_属性被赋给了$.fn,而$.fn表示了zepto对象的所有可用方法,而传入的dom和selector均为空,则dom的selector为空,下面编写代码验证一下
console.log($());
结果:
可以看到验证了以上所述
第二步,如果selctor是个string,并且是个html串,则调用zepto.fragment
zepto.fragment = function(html, name, properties) {
var dom, nodes, container
// 如果是类似于<div id="test" />,则先创建一个该node
if (singleTagRE.test(html)) dom = $(document.createElement(RegExp.$1))
if (!dom) {//如果node非空,则将类似于<div id="test" />替换为<div id="test"></div>
if (html.replace) html = html.replace(tagExpanderRE, "<$1></$2>")
if (name === undefined) name = fragmentRE.test(html) && RegExp.$1//取得标签名,例如div
// containers = {
// 'tr': document.createElement('tbody'),
// 'tbody': table, 'thead': table, 'tfoot': table,
// 'td': tableRow, 'th': tableRow,
// '*': document.createElement('div')
//},
if (!(name in containers)) name = '*'//设置容器标签名,如果不是tr,tbody,thead,tfoot,td,th,则容器标签名为div
container = containers[name]
container.innerHTML = '' + html
dom = $.each(slice.call(container.childNodes), function(){//循环遍历每个子节点
container.removeChild(this)
})
}
//如果properties是原始对象
if (isPlainObject(properties)) {
nodes = $(dom)//将dom转成zepto对象,为了方便下面调用zepto上的方法
$.each(properties, function(key, value) {
// methodAttributes = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset'],
if (methodAttributes.indexOf(key) > -1) nodes[key](value)
else nodes.attr(key, value)
})
}
//返回将字符串转成的DOM节点后的数组,比如'<li></li><li></li><li></li>'转成[li,li,li]
return dom
}
下面传入一个html字符串来进行验证:
不传入properties
var dom = $("<p>Hello</p>");
console.log(dom.text());
运行结果:
Hello
传入properties
var dom = $("<p />",
{
text:"Hello",
id:"greeting",
css:{color:'darkblue'}
});
console.log(dom.attr('id'));
console.log(dom.text());
console.log(dom.css('color'));
运行结果:
greeting
Hello
rgb(0, 0, 139)
第三步,如果存在context,则在context中查找
第四步,如果是选择器,则使用选择器进行查找
// `$.zepto.qsa` is Zepto's CSS selector implementation which
// uses `document.querySelectorAll` and optimizes for some special cases, like `#id`.
// This method can be overriden in plugins.
zepto.qsa = function(element, selector){
var found,
maybeID = selector[0] == '#',
maybeClass = !maybeID && selector[0] == '.',
nameOnly = maybeID || maybeClass ? selector.slice(1) : selector, // 从第2个字符开始得到剩下的字符串,如#test->test,.test->test
isSimple = simpleSelectorRE.test(nameOnly)//不是选择器集合
return (isDocument(element) && isSimple && maybeID) ?
( (found = element.getElementById(nameOnly)) ? [found] : [] ) ://by id
(element.nodeType !== 1 && element.nodeType !== 9) ? [] :
slice.call(
isSimple && !maybeID ?
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
)
}
第五步,如果selector是一个函数,则直接执行这个函数
实例:
$(function() {
console.log('hello world');
});
结果:
hello world
第六步,如果selector是一个zepto对象,则直接返回此selector
// `$.zepto.isZ` should return `true` if the given object is a Zepto
// collection. This method can be overriden in plugins.
zepto.isZ = function(object) {
return object instanceof zepto.Z
}
上述代码就是用来判断是否为zepto对象
第七步,如果selector是一个数组,则dom对象就等于此数组中的非空值组合
function isArray(value) { return value instanceof Array }
上述代码用来判断对象是否为数组
第八步,如果selector是一个对象,则dom对象就等于此对象构成的一个数组
function isObject(obj) { return type(obj) == "object" }function type(obj) {
return obj == null ? String(obj) :
class2type[toString.call(obj)] || "object"
}
上述代码用来判断是否为一个对象
综上,总的流程是根据selector为空,selector为string类型、selector为函数、selector为数组、selector为zepto对象、selector为对象等进行处理
本文详细解析了轻量级JavaScript库Zepto的核心方法$()的实现原理,包括空选择器处理、HTML片段转换、上下文选择及特殊场景优化。
518

被折叠的 条评论
为什么被折叠?



