对于jQuery的调用,咱们通常都会传入参数html
html: <div></div> js: console.log($('<a />')); console.log($('div'));
仔细观察返回的jQuery对象,感受像是对DOM节点进行封装,并将其保存在jQuery上前端
//获取到传入的参数 var jQuery = function(selector, content) { return new jQuery.prototype.init(selector, content); } jQuery.prototype = { length: 0, init: function(selector, content) { content = content || document; var match; //match 用来保存selector; if(typeof selector === 'string') { // 判断selector传入的是一个html标签; if(selector.charAt(0) === '<' && selector.charAt(selector.length-1) === '>' && selector.length >= 3 ) { match = [selector]; } if (match) { //建立一个jQuery对象。 } } } }
var reg = /^<(\w+)\s*\/?>(?:<\/\1>|)$/; jQuery.extend({ parseHtml: function(data, content) { if (!data && typeof data != 'string') { return null; } var parse = reg.exec(data); return [content.createElement(parse[1])] //ok,这里是将建立的一整个DOM节点保存在了数组中。 } })
if(match) { for(; i < match.length; i++) { var opt = match[i] this[i]=jQuery.parseHtml(opt, content)[i] } }
好了,如今咱们就能够看下代码的执行结果了。
ok,勉强能够实现,但好像仍是有点不对的样子, 让咱们来看下jQuery的源码。
能够看出是调用了merge方法。跳转到merge方法查看一下,通常来讲,merge用于合并两个数组, 也能够用于将数组合并在有length属性的对象上。jquery
// push.apply(_, arraylike) throws on ancient WebKit merge: function (first, second) { var len = +second.length, j = 0, i = first.length; for (; j < len; j++) { first[i++] = second[j]; } first.length = i; return first; }了解的区别后,咱们来优化代码吧,
优化数组
var ele, i = 0; if(match) {} else { ele = document.querySelectorALL(selector); for(; i < ele.length; i++) { this[i] = ele[i] } this.length = ele.length; }
var rootjQuery; init = jQuery.fn.init = function(selector, context, root) { ... root = root || rootjQuery; if(typeof selector === 'string') { ... } else if (isFunction(selector)) { return root.ready !== undefined ? root.ready(selector) : selector(jQuery); } } rootjQuery = jQuery(document);
代码能够看出,其实js是能够传递三个参数的,且root默认为document,若是root.ready没有初始话的话就马上执行传入的方法,不然调用root.ready方法。关于root,从代码能够看出,是个全局,那么root.ready == jQuery.ready。promise
function completed() { document.removeEventListener('DOMContentLoaded',completed); window.removeEventListener('load', completed); jQuery.ready(); } if(document.readyState === 'complete' || (document.readyState !== "loading" && !document.documentElement.doScroll)) { window.setTimeout(jQuery.ready); } else { //dom加载完毕后,调用complate移除监听事件。 document.addEventListener('DOMContentLoaded', complete); window.addEventListener('load', completed); }
jQuery.extend({ // DOM是否已经准备好要使用了,发生更改,则修改成true; isReady: false, //跟踪就绪事件触发前要等待的项目数计数器 readyWait: 1, ready: function(wait) { if(wait === true ? --jQuery.readyWait : jQuery.isReady) { return ; } //DOM节点已经准备好了 jQuery.isReady = true; if(wait !== true && --jQuery.readyWait > 0) { return } //若是有函数绑定,当即执行。 readyList.resolveWidth(document, [jQuery]) } })
从2.的时候能够看出,在DOM节点加载完的时候,调用了一次ready,此时没有传入wait,ready中第一个判断直接跳过,记录jQuery.isReady = true,而后再看下一句执行,调用readyList.resolveWidth方法,此时DOM节点已经加载完毕,能够执行绑定的函数了,
关于这个readyList是什么,咱们在代码中找一下看下,app
var readyList = jQuery.Deferred(); jQuery.fn.ready = function(fn) { ready.then(fn).catch(error) { jQuery.readyException(error); } return this; }
从以上代码能够看出,readyList是Deferred函数的返回值,且从下面的调用,能够推测Defferred函数应该是个promise对象。对于Defferred函数,下次再仔细研究一下。
如今让咱们来回顾整个函数流程:
上面的内容只是我本身的理解,若是有什么不对的地方,但愿你们帮忙指出啊!dom