jquery源码学习
TRANSCRIPT
jQuery 源码学习
qijun.weiqj
大纲• jQuery 无架构 *• 常用的静态方法• 解密万能构造器 *• 理解 jQuery 对象 *• 深入事件处理 *
jQuery 无架构
源码之前,了无秘密
var jQuery = function(selector, context) {
};
jQuery.extend = function(target, source) {// 回忆 FD.common.apply
};
于是我们有了静态方法 $.extend(… 因为函数也是对象
接着我们就可以扩展静态方法:$.extendIf = function(target, src) {}// 注:丹侠在 fdev4 中提供了此方法,以此眷顾我们的 fdev3
当然也可以这样$.extend($, {
extendIf: function() {…
}add: function() {
…}
})
可以更专业:$.extend({
extendIf: function()…add: function()…
})因为可以省略第一个参数 , 此时 target 就是 this, 即 jQuery
在这里我们初步感觉到 jQueryAPI 的特点:精简、灵活、还很智能, jQuery 的流行最大的原因就在于此
jQuery 中所有静态方法就是这样扩展的
注:但是我们把扩展 jQuery 的大任交给 fdev-4 维护小组,以避免名字空间冲突。应用中一般情况下不允许扩展 jQuery 对象
摘录:
jQuery.extend({noConflict: function(deep) {
isFunction: function( obj ) {return jQuery.type(obj) === "function";
},
isArray: Array.isArray || function( obj ) {return jQuery.type(obj) === "array";
},
isWindow: function( obj ) {return obj && typeof obj === "object" && "setInterval" in obj;
},
isNaN: function( obj ) {return obj == null || !rdigit.test( obj ) || isNaN( obj );
},
在 jQuery 中,我们可以这样:
$(‘#div’).addClass(‘error’);$(‘ul.tabs’, elm).eq(0).click();
从上面我们知道 jQuery 是一个函数, 从这里我们知道函数返回值是一个对象,并且有很多的实例方法, 这些方法
用来操作相关节点
var jQuery = function() {return new jQuery.fn.init(selector, context);
};
jQuery.fn = { };jQuery.fn.init = function() {};
jQuery.fn.init.prototype {addClass: function() {},removeClass: function() {}
};从现在开始,我们的 jQuery 对象也有了 addClass 和
removeClass 方法
重构一下:
var jQuery = function() {return new jQuery.fn.init(selector, context);
}jQuery.fn = {
init: function() {},addClass: function() {},removeClass: function() {}
}jQuery.fn.init.prototype = jQuery.fn
现在我们就可以扩展 jQuery 实例方法了$.fn.ajax = function(url, options) {}
专业点:$.extend($.fn, {
get: function()…post: function()…
})
更专业$.fn.extend({
bind: function() {…}})
因为: $.fn.extend = $.extend = function(…,
关于链式调用
$.fn.extend({addClass: function() {
…return this;
}toogleClass: function() {
…return this;
}hasClass: function() {
return true|false;}
经验建议
为了增加可读性, 避免不同类型 API 之间的链试调用
var elm = $(‘#id’); // 为了增加可读性, 避免过多的 $, 只在合适的地方使用 $ 构造
elm.removeClass(‘error’).addClass(‘success’);elm.click(function() {
…});
轻松一下:浏览一下常用静态方法工具方法
$.noConflict // 由 fdev-v4 默认调用 , 以兼容 fdev-v3 所以应用中使用jQuery 应该在一个 closure 中
$.trim
$.each —— 可以对对象和数组进行迭代$.extend —— FD.common.apply / YAHOO.lang.augmentObject$.extendIf
$.grep —— $.map
$.now —— new Date().getTime() , 方便加时间戳
数组操作和类型检测
$.inArray // 可以使用 Array.indexOf 代替$.makeArray // 很有用, 把任何东西变成数组$.merge // 可惜了这个方法
$.type // 返回类型字符串$.isArray$.isEmptyObject$.isFunction$.isPlainObject$.isWindow$.isXMLDoc
AJAX 和参数
$.param$.paramSpecial 中文 jsonp 调用代替 $.param$.unparam$.parseJSON$.parseXML$.globalEval $.ajax$.ajaxSetup
以下方法调用 $.ajax$.get$.post$.getJSON$.getScript
特性检测 / 浏览器检测 / 函数包装
$.support$.browser
关于浏览器检测,文档中有一句话很有价值:We recommend against using this property; please try to use feature detection insteadjQuery.browser may be moved to a plugin in a future release of jQuery.
$.error —— 当作 assert 使用$.noop —— 空方法$.proxy —— 包装一个函数,改变 this 作用域$.when$.sub
虽然在 api 文档中有,但不常用的方法 ,一般应用于 jQuery 内部,被相应的实例方法调用
$.fx
$.data$.hasData$.removeData$.contains $.queue$.dequeue$.cssHooks
fdev-v4 提供的静态方法
$.add$.use$.namespace
$.extendIf
$.unparam$.paramSpecial
$.util.cookie$.util.subCookie
$.util.substitute
万能构造器
• jQuery 对象是如何构造的• jQuery 对象是数组吗?• 构造成本有多大
回顾一下
var jQuery = function() {return new jQuery.fn.init(selector, context);
}jQuery.fn = {
init: function(selector, context) {},
1. $(null|false|…);2. $(element);3. $(‘body’)
init: function(selector, context) (// 1. 支持空 selectorif (!selector) {
return this;}
// 2. 原生 dom 节点if (selector.nodeType) {
this.context = this[0] = selector;this.length = 1;return this;
}
// 3. 字符串 bodyif (selector === 'body') {
this.context = document;this[0] = document.body;this.selector = 'body';this.length = 1;return this;
}
现在我们知道 :
jQuery 对象是普通的 Object, 不是 Array, 只是包含的 property 为数字,所以看起来像数组
4. $(function() {console.debug(‘domready’)
})5. $(array|collection)
//--------------------------------------------------------------------------------------// 4. function} else if (jQuery.isFunction(selector)) {
return jQuery.ready(selector);// 等效于 $(document).ready(function()…
}
// 5. collection / arrayreturn jQuery.makeArray(selector, this);
6. $('<div></div>')7. $('<div>')8. $('<div />')
selector = [ doc.createElement( ret[1] ) ];
9. $('<img>', {width: '100',height: '200'
});selector = [ doc.createElement( ret[1] ) ];jQuery.fn.attr.call( selector, context, true );
所以上述相当于 $(‘<img>’).attr(…
10. html 片段和 id 选择器
$(‘<a href=“#”> 这个 html 很复杂 </a>')
jQuery.buildFragment( [ match[1] ], [ doc ] );
1. 如果片段小于 0.5K, 可 cache, 以加快创建速度2. createDocumentFragment() 创建节点3. innerHTML 或 createTextNode 来创建内容
11. $(‘#id’) // 注意,这里需要 # , 因为要使用 CSS3 选 择器elem = document.getElementById( match[2] );
12. css3 选择器
$('div.myclass', [context]) ---> $(context).find('div.myclass') ----> $(context). pushStack(… 这一步会构造一个新的 jQuery 对象 -----> jQuery.find === Sizzle;
所以采用 css 3 选择器的时候, 实际上会生成两个 jQuery 对象不过不用担心, jQuery 对象很小, 下面看看它的成本。
jQuery 对象属性内存结构
看以上结构知道, jQuery 对象是简单的轻量级对象,构造成本很小,并且 GC 对小对象的回收效率非常高,所以从这上面讲不用担心效率和内存问题。
我在自己机子上构造了 10 万个 jQuery 对象,大概占用 32M 内存空间。
深入 jQuery 事件模型页面中有下面代码:<a href="#" class="link-a">Click Me A</a><a href="#" class="link-b">Click Me B</a>
<script>(function($) {
$('a.link-a').click(function() {console.debug('click a');
});$('a.link-a').click(function() {
console.debug('click a2');});$('a.link-a').dblclick(function() {
console.debug('dbclick');});
$('a.link-b').click(function() {console.debug('click other');
});
})(jQuery);</script>
在 firebug 的脚本监控中输入 jQuery.cache
再查看节点的 DOM
在源码中有这样一个字段: 1328 行jQuery.extend({
expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
一个节点一个事件一个 handler
2197 行:elemData.handle = eventHandle = function() {
return jQuery.event.handle.apply( eventHandle.elem, arguments ) :};
2250 行if ( elem.addEventListener ) {
elem.addEventListener( type, eventHandle, false );} else if ( elem.attachEvent ) {
elem.attachEvent( “on” + type, eventHandle );}
那么 jQuery.event.handle 应该是真正调用用户操作的地方
将上图数据结构展开
有了上图基础,看 jQuery 事件代码就容易多了, jQuery 的事件也就不再神秘
事件冒泡 live & delegate
问题 :1. 有何区别2. 如果实现
其实是一个问题: 事件最终挂接到哪里?
还是源码
delegate: function( selector, types, fn) {return this.live( types, fn, selector); <----- 第三个参数 jq 内部使用
}
jQuery.fn.live = function(types, fn, selector) {var context = selector ? this : $(this.context);…for ( var j = 0, l = context.length; j < l; j++ )
jQuery.event.add( context[j], "live." + liveConvert( type, }
总结:1. live 事件挂接在 jQuery 对象的 context 上(这里的 context 不是
jQuery 的第二个参数)一般为 document2. delegate 事件挂接在当前 jQuery 对象包含的节点上
简单地说:1. live挂接在 document 上(适合不复杂的冒泡)2. delegate挂接在指定节点上 (比较精细 )
谢谢• 谁教我做 PPT啊!