1. 那些年一直用jQuery處理事件,這些年想了解下內部原理嗎
說起jQuery的事件,不得不提一下Dean Edwards大神 addEvent庫,很多流行的類庫的基本思想從他那兒借來的
jQuery的事件處理機制吸取了javaScript專家Dean Edwards編寫的事件處理函數的精華,使得jQuery處理事件綁定的時候相當的可靠。
在預留退路(graceful degradation),循序漸進以及非入侵式編程思想方面,jQuery也做的非常不錯
事件的流程圖
總的來說對於JQuery的事件綁定
在綁定的時候做了包裝處理
在執行的時候有過濾器處理
.on( events [, selector ] [, data ], handler(eventObject) )
events:事件名
selector : 一個選擇器字元串,用於過濾出被選中的元素中能觸發事件的後代元素
data :當一個事件被觸發時,要傳遞給事件處理函數的
handler:事件被觸發時,執行的函數
例如:
var body = $('body')
body.on('click','p',function(){
console.log(this)
})
用on方法給body上綁定一個click事件,冒泡到p元素的時候才出發回調函數
這里大家需要明確一點:每次在body上點擊其實都會觸發事件,但是只目標為p元素的情況下才會觸發回調handler
通過源碼不難發現,on方法實質只完成一些參數調整的工作,而實際負責事件綁定的是其內部jQuery.event.add方法
on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
var origFn, type;
// Types can be a map of types/handlers
if ( typeof types === "object" ) {
// ( types-Object, selector, data )
if ( typeof selector !== "string" ) {
// ( types-Object, data )
data = data || selector;
selector = undefined;
}
for ( type in types ) {
this.on( type, selector, data, types[ type ], one );
}
return this;
}
if ( data == null && fn == null ) {
// ( types, fn )
fn = selector;
data = selector = undefined;
} else if ( fn == null ) {
if ( typeof selector === "string" ) {
// ( types, selector, fn )
fn = data;
data = undefined;
} else {
// ( types, data, fn )
fn = data;
data = selector;
selector = undefined;
}
}
if ( fn === false ) {
fn = returnFalse;
} else if ( !fn ) {
return this;
}
if ( one === 1 ) {
origFn = fn;
fn = function( event ) {
// Can use an empty set, since event contains the info
jQuery().off( event );
return origFn.apply( this, arguments );
};
// Use same guid so caller can remove using origFn
fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
}
return this.each( function() {
jQuery.event.add( this, types, fn, data, selector );
});
針對事件處理,我們可以拆分2部分:
一個事件預綁定期
一個事件執行期
本章著重講解事件的預綁定的時候做了那些處理,為什麼要這樣處理?
事件底層的綁定介面無非就是用addEventListener處理的,所以我們直接定位到addEventListener下面
jQuery.event.add 中有
elem: 目標元素
type: 事件類型,如』click』
eventHandle: 事件句柄,也就是事件回調處理的內容了
false: 冒泡
現在我們把之前的案例給套一下看看
var body = document.getElementsByTagName('body')
var eventHandle = function(){
console.log(this)
}
body .addEventListener( 'click』, eventHandle, false );
明顯有問題,每次在body上都觸發了回調,少了個p元素的處理,當然這樣的效果也無法處理
eventHandle源碼
回到內部綁定的事件句柄eventHandle ,可想而知eventHandle不僅僅只是只是充當一個回調函數的角色,而是一個實現了EventListener介面的對象
if ( !(eventHandle = elemData.handle) ) {
eventHandle = elemData.handle = function( e ) {
// Discard the second event of a jQuery.event.trigger() and
// when an event is called after a page has unloaded
return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ?
jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
undefined;
};
// Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
eventHandle.elem = elem;
}
可見在eventHandle中並沒有直接處理回調函數,而是映射到jQuery.event.dispatch分派事件處理函數了
僅僅只是傳入eventHandle.elem,arguments , 就是body元素 與事件對象
那麼這里有個問題,事件回調的句柄並沒有傳遞過去,後面的代碼如何關聯?
本章的一些地方可能要結合後面的dispatch處理才能理清,但是我們還是先看看做了那些處理
on內部的實現機制
我們開從頭來理清下jQuery.event.add代碼結構,適當的跳過這個環節中不能理解的代碼,具體遇到在提出
之前就提到過jQuery從1.2.3版本引入數據緩存系統,貫穿內部,為整個體系服務,事件體系也引入了這個緩存機制
所以jQuery並沒有將事件處理函數直接綁定到DOM元素上,而是通過$.data存儲在緩存$.cahce上
第一步:獲取數據緩存
//獲取數據緩存
elemData = data_priv.get( elem );
在$.cahce緩存中獲取存儲的事件句柄對象,如果沒就新建elemData
第二步:創建編號
if ( !handler.guid ) {
handler.guid = jQuery.guid++;
}
為每一個事件的句柄給一個標示,添加ID的目的是 用來尋找或者刪除handler,因為這個東東是緩存在緩存對象上的,沒有直接跟元素節點發生關聯
第三步:分解事件名與句柄
if ( !(events = elemData.events) ) {
events = elemData.events= {};
}
if ( !(eventHandle = elemData.handle) ) {
eventHandle = elemData.handle = function( e ) {
// Discard the second event of a jQuery.event.trigger() and
// when an event is called after a page has unloaded
return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ?
jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
undefined;
};
eventHandle.elem = elem;
}
events,eventHandle 都是elemData緩存對象內部的,可見
在elemData中有兩個重要的屬性,
一個是events,是jQuery內部維護的事件列隊
一個是handle,是實際綁定到elem中的事件處理函數
之後的代碼無非就是對這2個對象的篩選,分組,填充了
第四步: 填充事件名與事件句柄
// Handle multiple events separated by a space
// jQuery(...).bind("mouseover mouseout", fn);
// 事件可能是通過空格鍵分隔的字元串,所以將其變成字元串數組
// core_rnotwhite:/\S+/g
types = ( types || "" ).match( core_rnotwhite ) || [""];
// 例如:'.a .b .c'.match(/\S+/g) → [".a", ".b", ".c"]
// 事件的個數
t = types.length;
while ( t-- ) {
// 嘗試取出事件的命名空間
// 如"mouseover.a.b" → ["mouseover.a.b", "mouseover", "a.b"]
tmp = rtypenamespace.exec( types[t] ) || [];
// 取出事件類型,如mouseover
type = origType = tmp[1];
// 取出事件命名空間,如a.b,並根據"."分隔成數組
namespaces = ( tmp[2] || "" ).split( "." ).sort();
// There *must* be a type, no attaching namespace-only handlers
if ( !type ) {
continue;
}
// If event changes its type, use the special event handlers for the changed type
// 事件是否會改變當前狀態,如果會則使用特殊事件
special = jQuery.event.special[ type ] || {};
// If selector defined, determine special event api type, otherwise given type
// 根據是否已定義selector,決定使用哪個特殊事件api,如果沒有非特殊事件,則用type
type = ( selector ? special.delegateType : special.bindType ) || type;
// Update special based on newly reset type
// type狀態發生改變,重新定義特殊事件
special = jQuery.event.special[ type ] || {};
// handleObj is passed to all event handlers
// 這里把handleObj叫做事件處理對象,擴展一些來著handleObjIn的屬性
handleObj = jQuery.extend({
type: type,
origType: origType,
data: data,
handler: handler,
guid: handler.guid,
selector: selector,
needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
namespace: namespaces.join(".")
}, handleObjIn );
// Init the event handler queue if we're the first
// 初始化事件處理列隊,如果是第一次使用,將執行語句
if ( !(handlers = events[ type ]) ) {
handlers = events[ type ] = [];
handlers.delegateCount = 0;
// Only use addEventListener if the special events handler returns false
// 如果獲取特殊事件監聽方法失敗,則使用addEventListener進行添加事件
if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
if ( elem.addEventListener ) {
elem.addEventListener( type, eventHandle, false );
}
}
}
// 特殊事件使用add處理
if ( special.add ) {
special.add.call( elem, handleObj );
// 設置事件處理函數的ID
if ( !handleObj.handler.guid ) {
handleObj.handler.guid = handler.guid;
}
}
// Add to the element's handler list, delegates in front
// 將事件處理對象推入處理列表,姑且定義為事件處理對象包
if ( selector ) {
handlers.splice( handlers.delegateCount++, 0, handleObj );
} else {
handlers.push( handleObj );
}
// Keep track of which events have ever been used, for event optimization
// 表示事件曾經使用過,用於事件優化
jQuery.event.global[ type ] = true;
}
// Nullify elem to prevent memory leaks in IE
// 設置為null避免IE中循環引用導致的內存泄露
elem = null;
},
這段比較長了分解下,最終的目的就是為填充events,eventHandle
2. 那些年用jquery處理事件,這些年想知道內部原理嗎
說起jQuery的事件,不得不提一下Dean Edwards大神 addEvent庫 ,很多流行的類庫的基本思想從他那兒借來的
jQuery的事件處理機制吸取了JavaScript專家Dean Edwards編寫的事件處理函數的精華,使得jQuery處理事件綁定的時候相當的可靠。
在預留退路(graceful degradation),循序漸進以及非入侵式編程思想方面,jQuery也做的非常不錯
總的來說對於JQuery的事件綁定
在綁定的時候做了包裝處理
在執行的時候有過濾器處理
.on( events [, selector ] [, data ], handler(eventObject) )
selector : 一個選擇器字元串,用於過濾出被選中的元素中能觸發事件的後代元素
data :當一個事件被觸發時,要傳遞給事件處理函數的
handler:事件被觸發時,執行的函數
var body = $('body')
body.on('click','p',function(){
console.log(this)
})
用on方法給body上綁定一個click事件,冒泡到p元素的時候才出發回調函數
這里大家需要明確一點: 每次在body上點擊其實都會觸發事件,但是只目標為p元素的情況下才會觸發回調handler
通過源碼不難發現,on方法實質只完成一些參數調整的工作,而實際負責事件綁定的是其內部jQuery.event.add方法,轉載,僅供參考。
3. jquery有兩個版本,分別是proction和development,請問兩者之間的的區別是什麼
一個用在生產環境,也就是正式的線上,代碼是壓縮過的,應一個沒壓縮,你可以分析學習jquery源代碼!
4. JQuery的源碼看過嗎能不能簡單說一下它的實現原理
沒別的,就是封裝javascript,使調用更簡單而已。
5. 如何高效地閱讀 jQuery 源碼
嘗試把jquery拆分為ecma擴展(以及emitter promise和queue),dom兼容性問題(compressed && gziped 小於14k,嘗試理解哪些函數我不提供兼容性就不能使用,哪些我可以接受原生寫法,同時包括一個自己寫的css3選擇器) , 鏈式調用(嘗試理解用戶需求,理解怎麼寫方便),動畫庫(你是不是可以jq和css3兼容呢)
6. 各位大俠們,我正在學習jquery,但是當學到Ajax的時候感覺完全找不到頭緒,希望各位給指點一下啊
什麼都不用,只要jquery開發文檔api就好了,我就是這樣學過來的
7. jquery源碼 ,jquery選擇器,javascript,正則表達式
quickExpr應該是個二義正則。前半段是:
^[^<]*(<[\w\W]+>)[^>]*$:我猜測意思是一個簡單的標簽。如$('<div>new Div</div>')匹配的用法。
後半段是:
^#([\w-]+)$:這個顯然就是id選擇器。->getElementById
注意兩個正則用了|來分隔二義,所以你這里的匹配不成功是正常的。
jquery應該是根據這個quickExpr來判斷
if(match[1]) createNewfragment();....
else if(match[2]) getElementById();
當然這只是個人猜測,沒有細讀jquery源碼。僅做參考
8. 如何正確閱讀jquery源碼和jquery插件源碼
1. jQuery 裡面有很多東西是出於兼容性,歷史遺留。
比如 .ready() 之類的函數,為什麼會很scroll 有關,那是為了兼容某些ie。這種代碼對於編程思想來說不僅沒用,而且是雜音,你要篩選出來就得了解這段代碼的變動,費心費力得不償失。
2. jQuery 裡面的代碼不一定是最優的,例如事件委託,每一次事件觸發都要調用選擇器,實際上是效率很低的。但是我又比較懶,沒有提交patch。
3. jQuery 實際上很容易寫出來一個 barebone alternative,在使用的過程中多想,多思考多總結就可以了。
4. jQuery 這類框架里真正有思維挑戰性的東西不多,一半以上是堆代碼而已,剩下的一點價值在於架構、抽象、擴展能力。
5. 我有一句話與所有的同行分享:工程師讓需求成為現實,優秀工程師化復雜為簡單,頂尖工程師變不可能為可能;架構師掌握現在,優秀架構師展望未來,頂尖架構師創造時代。
jQuery 的設計目的是,讓前端工程師的工作更簡單更輕松,但它並不適合所有的前端工程師,假如你的目標是成為優秀架構師、頂尖架構師的話,你在jQuery里也看不清未來。
9. jquery的源碼看過嗎能不能簡單概況一下它的實現原理
原理就是對常用操作的封裝,順便解決了兼容性問題
10. jQuery 源代碼看不懂,怎麼辦。。有沒有解釋jQuery 源代碼的書籍
要不要閱讀別人代碼?
要。
閱讀別人代碼干什麼?
提高自己的代碼質量。
試圖通過閱讀別人代碼找出代碼的邏輯?
錯誤。
試圖通過閱讀別人代碼找出想要實現自己的邏輯的代碼?
正確,只有遵循了這個原則,才能實現物為我所用。
毫無目的去看別人代碼 不暈才怪呢。