jQuery源码分析1

写在开头:

  昨天开始,我决定要认真的看看jQuery的源码,选择1.7.2,源于公司用的这个版本。由于源码比较长,这将会是一个比较持久的过程,我将要利用业余时间,和偶尔上班不算忙的时间来进行。其实原本是打算对着源码抄一遍,将对其的理解写成注释,这也算是在强行堆代码量了吧(我想我这是有多懒,必须要反省)。不过鉴于自己平时比较懒惰的可耻行径,和太多的东西都写在一起有点庞大,我想我还是有必要写成一个专栏,来记录这个过程。其实最根本的原因是:源码里都是有注释的,而且注释写得那么详尽,翻译过来就是了,但是是否真正的理解了呢?还有它涉及到的其他的知识点,都是有必要去研究一下的。

正文开始:

  首先,抬头的注释里有一句 Includes Sizzle.js ,关于Sizzle.js,后面再来研究。

  整个jQuery的代码是写在一个(function(){})(window);的一个闭包函数里,用一个用匿名函数并加()进行运行,统一命名空间,防止变量的污染。匿名函数定义了两个参数window,
undefined,然后在执行时将window和window.undefined传给函数。将window作为参数传入,在函数里就可以直接调用window,而不必去找最外层的对象,这样在对window进行操作或者调用window的一些方法、属性时效率要高一些。Javascript 中的 undefined 并不是作为关键字,使用参数undefined 是为了防外面定义undefined变量而受污染(调用时第二个参数未写,即是传入了最原始的window.undefied)。

  然后再是定义一些变量存储常用的一些属性和方法(同样是为了便于不必每次都去查找)。

  在jQuery的构造函数(这样讲比较明了)返回的是一个jQuery.fn.init的对象。也就是说,new
jQuery()和jQuery()返回的都是jQuery.fn.init的对象,所以我们在使用jQuery的时候,通常是直接$()的方式创建jQuery对象。然后通过jQuery.fn.init.prototype=jQuery.fn=jQuery.prototype来将jQuery的原型和jQuery.fn.init的原型联系在一起,这样的写法看着会有点绕,但是其实也就是为了让返回的jQuery.fn.init的对象可以调用jQuery.prototype里定义的那些方法和属性。

  在init里主要就是针对不同的选择器返回对应的jQuery.fn.init对象。


/*!
* ...省略...
* Includes Sizzle.js
* http://sizzlejs.com/
* ...省略...
*/
(function (window, undefined) {
//用变量存储window的属性
var document = window.document,
navigator = window.navigator,
location = window.location;
//定义jQuery函数,并立即执行(//TODO)
var jQuery = (function () {

//定义一个局部变量,它对应jQuery(最终被return)
var jQuery = function (selector, context) {

//返回一个jQuery.fn.init对象(返回的是一个new之后的对象,
//这样就可以避免创建jQuery对象的时候写成 new jQuery()或者new $()...)
return new jQuery.fn.init(selector, context, rootjQuery);
},

//存储window.jQuery,以防万一被覆盖
_jQuery = window.jQuery,
_$ = window.$,

//指向root jquery的引用
rootjQuery,

//...省略一系列的正则...

//供于jQuery.camelCase回调,转换成驼峰式
fcamelCase = function (all, letter) {
return (letter + "").toUpperCase();
},

//存储用户代理以便jQuery.browser使用(为了判断浏览器的类型及版本)
userAgent = navigator.userAgent,

//用来匹配引擎和浏览器版本
browserMatch,

//ready事件(dom加载完成之后)处理函数队列
readyList,

// The ready event handler
DOMContentLoaded,

// Save a reference to some core methods
toString = Object.prototype.toString,
hasOwn = Object.prototype.hasOwnProperty,
push = Array.prototype.push,
slice = Array.prototype.slice,//slice(start,end) 方法可从已有的数组中返回选定的元素。
trim = String.prototype.trim,
indexOf = Array.prototype.indexOf,

// [[Class]] -> type pairs //TODO-暂时我也不知干嘛的
class2type = {};

//定义jQuery.prototype,并赋给jQuery.fn(用jQuery.fn写jQuery扩展的原因所在)
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
init: function (selector, context, rootjQuery) {
var match, elem, ret, doc;

// Handle $(""), $(null), or $(undefined) ->返回本身
if (!selector) {
return this;
}

// Handle $(DOMElement) -> 如果是element ,就直接封装成jquery对象
if (selector.nodeType) {
this.context = this[0] = selector;
this.length = 1;
return this;
}

//如果是body ->这里document.body作为条件,应该是为了兼容问题
//可是今天我在主流浏览器里测试,都能够同时找到document.body和document.documentElement
if (selector === "body" && !context && document.body) {
this.context = document;
this[0] = document.body;
this.selector = selector;
return this;
}

if (typeof selector === "string") {
//HTML string
if (selector.charAt(0) === "<" && selector.charAt(selector.length - 1) === ">" && selector.length >= 3) {

//跳过检查
match = [null, selector, null];
} else {
//RegExpObject.exec(string)返回一个数组,其中存放匹配的结果。如果未找到匹配,则返回值为 null。
match = quickExpr.exec(selector);
}

//验证match, match[1](这里存放是html)为非的时候context也必须为非(这种情况是#id)
if (match && (match[1] || !context)) {

// HANDLE: $(html) -> $(array)
if (match[1]) {
//这里的context其实可以理解成selector的parentNode或者parent()
//context ->DOM对象
context = context instanceof jQuery ? context[0] : context;
//如果制定了context,就返回context.ownerDocument(这里是context当前所属的document) || context,否则返回document
doc = (context ? context.ownerDocument || context : document);

//匹配成独立的标签(不含有属性之类,比如<a></a>)
ret = rsingleTag.exec(selector);

if (ret) {
//方法jQuery.isPlainObject( object )用于判断传入的参数是否是“纯粹”的对象,即是否是用对象直接量{}或new Object()创建的对象
if (jQuery.isplainObject(context)) {
selector = [document.createElement(ret[1])];
jQuery.fn.attr.call(selector, context, true);
} else {
selector = [doc.createElement(ret[1])];
}
} else {
//缓存selector的html。
ret = jQuery.buildFragment([match[1]], [doc]);
//如果是缓存了的,就clone fragment(文档碎片节点在添加到文档树之后便不能再对其进行操作),否则就直接取fragment 的childNodes
selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes;
}
//将selector合并到this,返回
return jQuery.merge(this, selector);

// HANDLE: $("#id")
} else {
elem = document.getElementById(match[2]);

if (elem && elem.parentNode) {
//处理 IE and Opera 混淆ID与NAME的bug
if (elem.id !== match[2]) {
//调用Sizzle的方法 -- TODO,关于Sizzle.js,有待研究!
return rootjQuery.find(selector);
}

this.length = 1;
this[0] = elem;
}

this.context = document;
this.selector = selector;
return this;
}

// HANDLE: $(expr, $(...))
} else if (!context || context.jquery) {
return (context || rootjQuery).find(selector);

// HANDLE: $(expr, context)
} else {
return this.constructor(context).find(selector);
}

// HANDLE: $(function)
} else if (jQuery.isFunction(selector)) {
return rootjQuery.ready(selector);
}

//selector本身就是一个jQuery对象的情况
if (selector.selector !== undefined) {
this.selector = selector.selector;
this.context = selector.context;
}

//合并属性(与jQuery.merge不同的是,这里的selector可能不是数组)
return jQuery.makeArray(selector, this);
},

// Start with an empty selector
selector: "",

// The current version of jQuery being used
jquery: "1.7.2",

// The default length of a jQuery object is 0
length: 0,
       
       //TODO--未完待续

};

})();
})(window);

  

关于DocumentFragment:

  文档碎片是一种"轻量级"文档对象,可以包含和控制节点(同其他文档对象一样),但不会像完整的文档那样占用额外资源,在添加大量的节点的情况下,可以将文档碎片当做一个缓存使用,把节点先存放到文档碎片中,再把DocumentFragment节点插入文档树(当把文档碎片插入文档树时,插入的是它的子孙节点),在要添加的节点量很大的情况下效率会要高很多。

参考资料:http://www.cnblogs.com/aaronjs/p/3510768.html#_h1_2  

  

源码下载:  http://jquery.com/

jQuery源码分析1,布布扣,bubuko.com

时间: 2024-10-12 22:36:36

jQuery源码分析1的相关文章

jQuery源码分析系列(33) : AJAX中的前置过滤器和请求分发器

jQuery1.5以后,AJAX模块提供了三个新的方法用于管理.扩展AJAX请求,分别是: 1.前置过滤器 jQuery. ajaxPrefilter 2.请求分发器 jQuery. ajaxTransport, 3.类型转换器 ajaxConvert 源码结构: jQuery.extend({ /** * 前置过滤器 * @type {[type]} */ ajaxPrefilter: addToPrefiltersOrTransports(prefilters), /** * 请求分发器 *

Jquery源码分析

1.概述 jQuery是一个非常优秀的Js库,与prototype,YUI,Mootools等众多的Js类库相比,它剑走偏锋,从web开发最实用的角度出发,抛除了一些中看但不实用的东西,为开发者提供一个短小精悍的类库.由于其个短小精悍,使用简单方便,性能相对高效.众多的开发者都选择Jquery来进行辅助的web开发. 在使用jquery时开发,我们也会时常碰到许多的问题,但是jquery的代码很晦涩,难起看懂,当开发时出现了问题,看不懂源码,不知道如何去排错. John Resig,Jquery

jquery源码分析(二)——结构

再来复习下整体架构: jQuery源码分析(基于 jQuery 1.11 版本,共计8829行源码) (21,94)                定义了一些变量和函数jQuery=function(){} (96,280)        给jQuery添加一些方法和属性,jQuery.fn=jQuery.prototype(285,347)        extend:        jQuery的一些继承方法        更容易进行后续的扩展                       

jQuery源码分析-jQuery中的循环技巧

Js代码   作者:nuysoft/JS攻城师/高云 QQ:47214707 EMail:[email protected] 声明:本文为原创文章,如需转载,请注明来源并保留原文链接. 前记:本文收集了jQuery中出现的各种遍历技巧和场景 Js代码   // 简单的for-in(事件) for ( type in events ) { } Js代码   // 缓存length属性,避免每次都去查找length属性,稍微提升遍历速度 // 但是如果遍历HTMLCollection时,性能提升非常

jQuery源码分析系列(38) : 队列操作

Queue队列,如同data数据缓存与Deferred异步模型一样,都是jQuery库的内部实现的基础设施 Queue队列是animate动画依赖的基础设施,整个jQuery中队列仅供给动画使用 Queue队列 队列是一种特殊的线性表,只允许在表的前端(队头)进行删除操作(出队),在表的后端(队尾)进行插入操作(入队).队列的特点是先进先出(FIFO-first in first out),即最先插入的元素最先被删除. 为什么要引入队列? 我们知道代码的执行流有异步与同步之分,例如 var a

jQuery源码分析:源码结构与核心函数

jQuery源码分析-03构造jQuery对象-源码结构和核心函数 jQuery.fn和jQuery.prototype区别

jQuery源码分析系列(36) : Ajax - 类型转化器

什么是类型转化器? jQuery支持不同格式的数据返回形式,比如dataType为 xml, json,jsonp,script, or html 但是浏览器的XMLHttpRequest对象对数据的响应只有 responseText与responseXML 二种 所以现在我要定义dataType为jsonp,那么所得的最终数据是一个json的键值对,所以jQuery内部就会默认帮你完成这个转化工作 jQuery为了处理这种执行后数据的转化,就引入了类型转化器,如果没有指定类型就依据响应头Con

jQuery源码分析系列(34) : Ajax - 预处理jsonp

上一章大概讲了前置过滤器和请求分发器的作用,这一章主要是具体分析每种对应的处理方式 $.ajax()调用不同类型的响应,被传递到成功处理函数之前,会经过不同种类的预处理(prefilters). 预处理的类型取决于由更加接近默认的Content-Type响应,但可以明确使用dataType选项进行设置.如果提供了dataType选项, 响应的Content-Type头信息将被忽略. 有效的数据类型是text, html, xml, json,jsonp,和 script. dataType:预期

jQuery源码分析-each函数

本文部分截取自且行且思 jQuery.each方法用于遍历一个数组或对象,并对当前遍历的元素进行处理,在jQuery使用的频率非常大,下面就这个函数做了详细讲解: 复制代码代码 /*! * jQuery源码分析-each函数 * jQuery版本:1.4.2 * * ---------------------------------------------------------- * 函数介绍 * * each函数通过jQuery.extend函数附加到jQuery对象中: * jQuery.