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的更多相关文章
- jQuery源码分析系列
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...
- [转] jQuery源码分析-如何做jQuery源码分析
jQuery源码分析系列(持续更新) jQuery的源码有些晦涩难懂,本文分享一些我看源码的方法,每一个模块我基本按照这样的顺序去学习. 当我读到难度的书或者源码时,会和<如何阅读一本书> ...
- jQuery 源码分析 8: 回头看jQuery的构造器(jQuery.fn,jQury.prototype,jQuery.fn.init.prototype的分析)
在第一篇jQuery源码分析中,简单分析了jQuery对象的构造过程,里面提到了jQuery.fn.jQuery.prototype.jQuery.fn.init.prototype的关系. 从代码中 ...
- [转]jQuery源码分析系列
文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...
- jquery源码分析之一前言篇
1.问:jquery源码分析的版本是什么? 答:v3.2.1 2.问:为什么要分析jquery源码? 答:javascript是一切js框架的基础,jquery.es6.vue.angular.rea ...
- jQuery源码分析-each函数
本文部分截取自且行且思 jQuery.each方法用于遍历一个数组或对象,并对当前遍历的元素进行处理,在jQuery使用的频率非常大,下面就这个函数做了详细讲解: 复制代码代码 /*! * jQuer ...
- jQuery源码分析系列(转载来源Aaron.)
声明:非本文原创文章,转载来源原文链接Aaron. 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAa ...
- jQuery源码分析系列——来自Aaron
jQuery源码分析系列——来自Aaron 转载地址:http://www.cnblogs.com/aaronjs/p/3279314.html 版本截止到2013.8.24 jQuery官方发布最新 ...
- jQuery源码分析系列(36) : Ajax - 类型转化器
什么是类型转化器? jQuery支持不同格式的数据返回形式,比如dataType为 xml, json,jsonp,script, or html 但是浏览器的XMLHttpRequest对象对数据的 ...
- jQuery源码分析-01总体架构
1. 总体架构 1.1自调用匿名函数 self-invoking anonymous function 打开jQuery源码,首先你会看到这样的代码结构: (function( window, und ...
随机推荐
- Python学习笔记七-错误和异常
程序员总是和各种错误打交道,学习如何识别并正确的处理程序错误是很有必要的. 7.1错误和异常 1.错误 从软件方面来看,错误分为语法错误和逻辑错误两种.这两种错误都将导致程序无法正常进行下去,当Pyt ...
- IE6\ IE7、IE8\9\10和Firefox的hack方式
#test{color:red;color:red !important;/ Firefox.IE7支持 */_color:red; / IE6支持 */*color:red; / IE6.IE7支持 ...
- 测试Beetle.Redis+Web Api
在Web Api里用Beetle.Redis调用Redis服务端的TIME命令返回一个json格式,然后客户端是用的HttpTest4Net做的测试: CPU:I7-3770 内存:4G+4G 硬盘: ...
- 点击类名方法名如何连接到相应的Android源代码
. 找到Android SDK所在路径 “D:\androidDev\android-sdk-windows\” . 在该目录下选择platforms到 “D:\androidDev\android- ...
- Linux系统编程(30)—— socket编程之TCP/IP协议
在世界上各地,各种各样的电脑运行着各自不同的操作系统为大家服务,这些电脑在表达同一种信息的时候所使用的方法是千差万别.就好像圣经中上帝打乱了各地人的口音,让他们无法合作一样.计算机使用者意识到,计算机 ...
- UML建模之时序图
现在是二月,而且到如今你或许已经读到.或听到人们谈论UML 2.0 —— 包括若干进步的 UML 的新规范,所做的变化.考虑到新规范的重要性,我们也正在修改这个文章系列的基础,把我们的注意力从 OMG ...
- (总结)工作中常用的js自定义函数——日期时间类
//设置时间类 var Wsdatatime = function(){ this.today = (new Date()).getTime(); //当前时间 } Wsdatatime.protot ...
- HDU_2014——计算平均分
Problem Description 青年歌手大奖赛中,评委会给参赛选手打分.选手得分规则为去掉一个最高分和一个最低分,然后计算平均得分,请编程输出某选手的得分. Input 输入数据有多组,每 ...
- Django之路由系统
一.路由系统介绍 在django程序中,可以通过urls.py文件对所有的url进行任务的分配,根据路由规则的定义选择不同的业务处理函数进行处理 二.路由规则定义 1.路由规则代码如下,mysite/ ...
- Javascript:splice()方法实现对数组元素的插入、删除、替换及去重
定义和用法 splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目. 注释:该方法会改变原始数组. 语法: Array.prototype.splice(index,count[,el ...