jquery技术内幕-总体架构
一、总体架构
- 各模块之间的依赖关系:
- 自调用匿名函数阅读:
- 为什么使用自调用匿名函数?防止变量污染其他js文件的调用,保证jq库的内部使用不受影响。
- 为什么传入window、undefined作为参数?一方面使其作为变量可以使jq内部访问window、undefined对象更快捷;另一方面,我不是很懂,原文“将window、undefined作为参数传入,可以在压缩代码是进行优化,在压缩文件中可以看到window和undefined分别变为了a和b”,另外,对于undefined来说,在某些浏览器中会被重写值,为了避免这一情况的发生,这里作为参数传入,可以保证undefined始终为undefined;
- 匿名函数调用尽量不要省略括号末尾的分号;
二、构造jquery对象
jq对象是一个“类数组”对象,含有length属性,连续整型属性和大量的jquery方法。
- jq构造函数的7种用法
- jq方法创建jq对象,$是jq方法的缩写,jq方法不同的参数创建对象逻辑也会不同,共7种。
- jQuery():length为0的对象
- jQuery(html[,ownerDocument])、 jQuery(html,props)
- jQuery(selector[,context])
- jQuery(element)、 jQuery(elementArray)
- jQuery(object)
- jQuery(callback)
- jQuery(jQuery object)
- 总体分析结构:
代码:
16(function( window, undefined ) {
// 构造 jQuery 对象
22 var jQuery = (function() {
25 var jQuery = function( selector, context ) {
27 return new jQuery.fn.init( selector, context, rootjQuery
28 },
// 一堆局部变量声明
97 jQuery.fn = jQuery.prototype = {
98 constructor: jQuery,
99 init: function( selector, context, rootjQuery ) { ... },
//一堆原型属性和方法
319 };
322 jQuery.fn.init.prototype = jQuery.fn;
324 jQuery.extend = jQuery.fn.extend = function() { ... };
388 jQuery.extend({
// 一堆静态属性和方法
892 });
955 return jQuery;
957 })();
//省略其他模块的代码
9246 window.jQuery = window.$ = jQuery;
9266})( window );
1)为什么要在构造函数jQuery()内部用运算符new创建并返回另一个构造函数的实例? 如果构造函数有返回值,运算符new所创建的对象会被丢弃,返回值将作为new表达式的值。创建jQuery对象时,可以省略运算符new直接写jQuery()。
2)为什么在第97行执行jQuery.fn=jQuery.prototype,设置jQuery.fn指向构造函数jQuery()的原型对象jQuery.prototype?jQuery.fn是jQuery.prototype的简写。
3)既然调用构造函数jQuery()返回的jQuery对象实际上是构造函数jQuery.fn.init()的实例,为什么能在构造函数jQuery.fn.init()的实例上调用构造函数jQuery()的原型方法和属性?jQuery.fn.init.prototype=jQuery.fn时,用构造函数jQuery()的原型对象覆盖了构造函数jQuery.fn.init()的原型对象,从而使构造函数jQuery.fn.init()的实例也可以访问构造函数jQuery()的原型方法和属性。
4)为什么要把第25~955行的代码包裹在一个自调用匿名函数中,然后把第25行定义的构造函数jQuery()作为返回值赋值给第22行的jQuery变量?去掉这个自调用匿名函数,直接在第25行定义构造函数jQuery()不也可以吗?去掉了不是更容易阅读和理解吗?通过把这些局部变量包裹在一个自调。通过把这些局部变量包裹在一个自调。
5)为什么要覆盖构造函数jQuery()的原型对象jQuery.prototype?在原型对象jQuery.prototype上定义的属性和方法会被所有jQuery对象继承,可以有效减少每个jQuery对象所需的内存。事实上,jQuery对象只包含5种非继承属性,其余都继承自原型对象jQuery.prototype;在构造函数jQuery.fn.init()中设置了整型属性、length、selector、context;在原型方法.pushStack()中设置了prevObject。因此,也不必因为jQuery对象带有太多的属性和方法而担心会占用太多的内存。
- 构造函数jq.fn.init的12个分支:jq.buildFragment+jq.clean+jq.extend+jq.fn.extend:
jQuery.buildFragment(args, nodes, scripts)执行的5个关键步骤如下:
1)如果HTML代码符合缓存条件,则尝试从缓存对象jQuery.fragments中读取缓存的DOM元素。
2)若上述执行不成功,则创建文档片段DocumentFragment。
3)调用方法jQuery.clean(elems, context, fragment, scripts)将HTML代码转换为DOM元素,并存储在创建的文档片段中。
4)如果HTML代码符合缓存条件,则把转换后的DOM元素放入缓存对象jQuery.fragments。
HTML代码必须满足以下所有条件,才认为符合缓存条件:
- 数组args的长度为1,且第一个元素是字符串,即数组args中只含有一段HTML代码。
- HTML代码的长度小于512(1/2KB),否则可能会导致缓存占用的内存过大。
- 文档对象doc是当前文档对象,即只缓存为当前文档创建的DOM元素,不缓存其他框架(iframe)的。 •HTML代码以左尖括号开头,即只缓存DOM元素,不缓存文本节点。
- HTML代码中不能含有以下标签:<script>、<object>、<embed>、<option>、<style>。
- 当前浏览器可以正确地复制单选按钮和复选框的选中状态checked,或者HTML代码中的单选按钮和复选按钮没有被选中。
- 当前浏览器可以正确地复制HTML5元素,或者HTML代码中不含有HTML5标签。
5)最后返回文档片段和缓存状态{fragment: fragment,cacheable: cacheable}。
其中使用要注意:
- 方法.size()在功能上等价于属性.length,但应该优先使用属性.length,因为它没有函数调用开销。
- 执行时可通过方法call()和apply()指定方法执行的环境,即关键字this所引用的对象。
- 静态方法jQuery.each(),对于数组和含有length属性的类数组对象(如函数参数对象arguments),该方法通过下标遍历,从0到length-1;对于其他对象则通过属性名遍历(for-in)。在遍历过程中,如果回调函数返回false,则结束遍历。


浙公网安备 33010602011771号