zepto = {};
这个变量贯穿始终,也是zepto与jQuery很不一样的地方,jQuery是一个类,会创建一个个实例,而zepto本身就只是一个对象......
$ = function(selector, context){ return zepto.init(selector, context)}
而我们开始便说了zepto只是一个对象,而zepto.init也仅仅是返回了一个类数组的东西,于是我们这里便看到了zepto与jQuery的惊人差异
第一观感是zepto没有类操作!我们使用$('')的操作返回的也是zepto的实例$对于zepto来说仅仅是一个方法,zepto却使用了非正规手法返回了实例......从这里看整个zepto其实和jQuery就差距大了,zepto的$方法返回了一个Object的实例,而jQuery的$返回的是真资格的jQuery对象而从后面看其实zepto也是返回的一个实例但是与jQuery的实现有所不同,那么zepto是怎么实现实例返回的呢?$.fn = {};zepto的第三部分便是扩展$函数,我们使用的$的方法事实上都是其静态方法,与原型链一毛钱关系都没有以上便是zepto核心模块的实现,很干净的实现,仅仅是dom操作,不涉及事件或者Ajax操作,简单来说zepto的实现是这个样子的
1 var zepto = {}, $; 2 3 zepto.init = function (selector, context) { 4 var domArr = []; 5 //这个__proto__是系统级变量,我觉得zepto不该重置 ,但是不重置的话实例便找不到方法了!!! 6 domArr.__proto__ = $.fn 7 domArr.selector = selector; 8 //一些列操作 9 return domArr;10 };11 12 $ = function (selector, context) {13 return zepto.init(selector, context);14 };15 16 $.fn = {17 addClass: function () { },18 attr: function () { }19 };
这里有段非常关键的代码是:
domArr.__proto__ = $.fn;如果是没有这段代码的话, domArr便是属于array的实例,便不能使用$.fn中的方法了,但是他这里重置了__proto__的指向所以就能用了PS:由于IE是不认这个属性的,所以IE必定会报错由于这里的改下,本来domArr也会有一些变化:
1 dom.__proto__.constructor 2 function Array() { [native code] } 3 4 dom.__proto__.constructor 5 function Object() { [native code] } 6 7 zepto.Z = function(dom, selector) { 8 dom = dom || [] 9 dom.__proto__ = $.fn10 dom.selector = selector || ''11 return dom12 }13 //最后加上一句:14 zepto.Z.prototype = $.fn如此一来,我们所有的$方法返回的东西其实就变成了zepto.Z的实例了,这里的实现原理其实也有点绕口:构造函数zepto.Z 包含一个原型 $.fn(zepto.Z的prototype被重写了)原型$.fn具有一个Constructor回值构造函数zepto.Z(这里由于其粗暴的干法其实直接指向了Object,这里关系其实已经丢失)比较不正经的是居然是通过重写__proto__实现,感觉怪怪的,好了核心模块介绍结束,我们便进入入口函数的解析了
1 $('div'); 2 //=> all DIV elements on the page 3 $('#foo'); 4 //=> element with ID "foo" 5 6 // create element: 7 $("<p>Hello</p>"); 8 //=> the new P element 9 // create element with attributes:10 $("<p />", {11 text: "Hello",12 id: "greeting",13 css: { color: 'darkblue' }14 });15 //=> <p id=greeting style="color:darkblue">Hello</p>16 17 // execute callback when the page is ready:18 $(function ($) {19 alert('Ready to Zepto!')20 });
我们现在来分析其每一种实现
zepto.qsa = function(element, selector){ 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) return (isDocument(element) && isSimple && maybeID) ? ( (found = element.getElementById(nameOnly)) ? [found] : [] ) : (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 )}View Code
$("<p>Hello</p>");
这里依旧会经过zepto.init的处理,判断是否具有尖括号(<),有的话便会进入神奇的fragment逻辑创建文档碎片
dom = zepto.fragment(selector, RegExp.$1, context)这里有一个正则表达式对传入的html进行解析,目标是标签名PS:zepto对p标签的解析也会出问题,不建议使用
zepto.fragment = function(html, name, properties) {}
到fragment方法时,会传入html和那么并且会有相关属性,但是我们一般不这样干,仅仅希望创建DOM
zepto.fragment = function(html, name, properties) { var dom, nodes, container // A special case optimization for a single tag if (singleTagRE.test(html)) dom = $(document.createElement(RegExp.$1)) if (!dom) { if (html.replace) html = html.replace(tagExpanderRE, "<$1></$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}View Code里面的逻辑各位自己去看,我这里不多说了,还是很简单的,大概的想法是创建一个空的div元素,将字符串装载,然后遍历div的子元素,最后返回一个node的集合数组,这个也就是我们实际需要的......这个样子,创建标签或者selector选择器得到的结果是一致的其它逻辑大同小异,我们直接就过了,zepto核心入口逻辑就到此结束了......
attr: function(name, value){ var result return (typeof name == 'string' && value === undefined) ? (this.length == 0 || this[0].nodeType !== 1 ? undefined : (name == 'value' && this[0].nodeName == 'INPUT') ? this.val() : (!(result = this[0].getAttribute(name)) && name in this[0]) ? this[0][name] : result ) : this.each(function(idx){ if (this.nodeType !== 1) return if (isObject(name)) for (key in name) setAttribute(this, key, name[key]) else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name))) })},function setAttribute(node, name, value) { value == null ? node.removeAttribute(name) : node.setAttribute(name, value)}我们看到他这里直接将其转换为了元素DOM操作,没有什么好说的,只是如果value不为undefined时,里面有一个循环为属性赋值的动作再看这里的html接口
html: function(html){ return arguments.length === 0 ? (this.length > 0 ? this[0].innerHTML : null) : this.each(function(idx){ var originHtml = this.innerHTML $(this).empty().append( funcArg(this, html, idx, originHtml) ) })},function funcArg(context, arg, idx, payload) { return isFunction(arg) ? arg.call(context, idx, payload) : arg}这里其实是先将this清空,然后装载新的dom结构,这里与设置innerHTML有所不同,appen
新闻热点
疑难解答