jquery源码学习

37
jQuery 源源源源 qijun.weiqj

Upload: fangdeng

Post on 22-Jun-2015

1.960 views

Category:

Documents


7 download

TRANSCRIPT

Page 1: jQuery源码学习

jQuery 源码学习

qijun.weiqj

Page 2: jQuery源码学习

大纲• jQuery 无架构 *• 常用的静态方法• 解密万能构造器 *• 理解 jQuery 对象 *• 深入事件处理 *

Page 3: jQuery源码学习

jQuery 无架构

源码之前,了无秘密

Page 4: jQuery源码学习

var jQuery = function(selector, context) {

};

jQuery.extend = function(target, source) {// 回忆 FD.common.apply

};

于是我们有了静态方法 $.extend(… 因为函数也是对象

Page 5: jQuery源码学习

接着我们就可以扩展静态方法:$.extendIf = function(target, src) {}// 注:丹侠在 fdev4 中提供了此方法,以此眷顾我们的 fdev3

当然也可以这样$.extend($, {

extendIf: function() {…

}add: function() {

…}

})

Page 6: jQuery源码学习

可以更专业:$.extend({

extendIf: function()…add: function()…

})因为可以省略第一个参数 , 此时 target 就是 this, 即 jQuery

在这里我们初步感觉到 jQueryAPI 的特点:精简、灵活、还很智能, jQuery 的流行最大的原因就在于此

jQuery 中所有静态方法就是这样扩展的

注:但是我们把扩展 jQuery 的大任交给 fdev-4 维护小组,以避免名字空间冲突。应用中一般情况下不允许扩展 jQuery 对象

Page 7: 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 );

},

Page 8: jQuery源码学习

在 jQuery 中,我们可以这样:

$(‘#div’).addClass(‘error’);$(‘ul.tabs’, elm).eq(0).click();

从上面我们知道 jQuery 是一个函数, 从这里我们知道函数返回值是一个对象,并且有很多的实例方法, 这些方法

用来操作相关节点

Page 9: 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 方法

Page 10: jQuery源码学习

重构一下:

var jQuery = function() {return new jQuery.fn.init(selector, context);

}jQuery.fn = {

init: function() {},addClass: function() {},removeClass: function() {}

}jQuery.fn.init.prototype = jQuery.fn

Page 11: jQuery源码学习

现在我们就可以扩展 jQuery 实例方法了$.fn.ajax = function(url, options) {}

专业点:$.extend($.fn, {

get: function()…post: function()…

})

更专业$.fn.extend({

bind: function() {…}})

因为: $.fn.extend = $.extend = function(…,

Page 12: jQuery源码学习

关于链式调用

$.fn.extend({addClass: function() {

…return this;

}toogleClass: function() {

…return this;

}hasClass: function() {

return true|false;}

Page 13: jQuery源码学习

经验建议

为了增加可读性, 避免不同类型 API 之间的链试调用

var elm = $(‘#id’); // 为了增加可读性, 避免过多的 $, 只在合适的地方使用 $ 构造

elm.removeClass(‘error’).addClass(‘success’);elm.click(function() {

…});

Page 14: jQuery源码学习

轻松一下:浏览一下常用静态方法工具方法

$.noConflict // 由 fdev-v4 默认调用 , 以兼容 fdev-v3 所以应用中使用jQuery 应该在一个 closure 中

$.trim

$.each —— 可以对对象和数组进行迭代$.extend —— FD.common.apply / YAHOO.lang.augmentObject$.extendIf

$.grep —— $.map

$.now —— new Date().getTime() , 方便加时间戳

Page 15: jQuery源码学习

数组操作和类型检测

$.inArray // 可以使用 Array.indexOf 代替$.makeArray // 很有用, 把任何东西变成数组$.merge // 可惜了这个方法

$.type // 返回类型字符串$.isArray$.isEmptyObject$.isFunction$.isPlainObject$.isWindow$.isXMLDoc

Page 16: jQuery源码学习

AJAX 和参数

$.param$.paramSpecial 中文 jsonp 调用代替 $.param$.unparam$.parseJSON$.parseXML$.globalEval $.ajax$.ajaxSetup

以下方法调用 $.ajax$.get$.post$.getJSON$.getScript

Page 17: jQuery源码学习

特性检测 / 浏览器检测 / 函数包装

$.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

Page 18: jQuery源码学习

虽然在 api 文档中有,但不常用的方法 ,一般应用于 jQuery 内部,被相应的实例方法调用

$.fx

$.data$.hasData$.removeData$.contains $.queue$.dequeue$.cssHooks

Page 19: jQuery源码学习

fdev-v4 提供的静态方法

$.add$.use$.namespace

$.extendIf

$.unparam$.paramSpecial

$.util.cookie$.util.subCookie

$.util.substitute

Page 20: jQuery源码学习

万能构造器

• jQuery 对象是如何构造的• jQuery 对象是数组吗?• 构造成本有多大

Page 21: jQuery源码学习

回顾一下

var jQuery = function() {return new jQuery.fn.init(selector, context);

}jQuery.fn = {

init: function(selector, context) {},

Page 22: jQuery源码学习

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;

}

Page 23: jQuery源码学习

// 3. 字符串 bodyif (selector === 'body') {

this.context = document;this[0] = document.body;this.selector = 'body';this.length = 1;return this;

}

现在我们知道 :

jQuery 对象是普通的 Object, 不是 Array, 只是包含的 property 为数字,所以看起来像数组

Page 24: jQuery源码学习

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);

Page 25: jQuery源码学习

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(…

Page 26: jQuery源码学习

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] );

Page 27: jQuery源码学习

12. css3 选择器

$('div.myclass', [context]) ---> $(context).find('div.myclass') ----> $(context). pushStack(… 这一步会构造一个新的 jQuery 对象 -----> jQuery.find === Sizzle;

所以采用 css 3 选择器的时候, 实际上会生成两个 jQuery 对象不过不用担心, jQuery 对象很小, 下面看看它的成本。

Page 28: jQuery源码学习

jQuery 对象属性内存结构

看以上结构知道, jQuery 对象是简单的轻量级对象,构造成本很小,并且 GC 对小对象的回收效率非常高,所以从这上面讲不用担心效率和内存问题。

我在自己机子上构造了 10 万个 jQuery 对象,大概占用 32M 内存空间。

Page 29: jQuery源码学习

深入 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>

Page 30: jQuery源码学习

在 firebug 的脚本监控中输入 jQuery.cache

再查看节点的 DOM

Page 31: jQuery源码学习

在源码中有这样一个字段: 1328 行jQuery.extend({

expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),

Page 32: jQuery源码学习

一个节点一个事件一个 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 应该是真正调用用户操作的地方

Page 33: jQuery源码学习

将上图数据结构展开

有了上图基础,看 jQuery 事件代码就容易多了, jQuery 的事件也就不再神秘

Page 34: jQuery源码学习

事件冒泡 live & delegate

问题 :1. 有何区别2. 如果实现

其实是一个问题: 事件最终挂接到哪里?

Page 35: jQuery源码学习

还是源码

delegate: function( selector, types, fn) {return this.live( types, fn, selector); <----- 第三个参数 jq 内部使用

}

Page 36: jQuery源码学习

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挂接在指定节点上 (比较精细 )

Page 37: jQuery源码学习

谢谢• 谁教我做 PPT啊!