1. 程式人生 > >jQuery原始碼分析之on方法

jQuery原始碼分析之on方法

測試程式碼1:

var data = { id: 5, name: "張三" };
var events = {
"mouseenter": function(event){
$(this).html( "你好," + event.data.name + "!");       
},
"mouseleave": function(event){
$(this).html( "再見!");
}       
};
//為n5繫結mouseenter mouseleave兩個事件,併為其傳入附加資料data
$("body").on(events, "#n5", data);
});
測試程式碼2:
  var eventsMap = {
"mouseenter": function(event){
$(this).html( "Hello!");        
},
"mouseleave": function(event){
$(this).html( "Bye!");
}
};
//為n5繫結mouseenter mouseleave兩個事件
 $("#n5").one( eventsMap );
 alert(typeof eventsMap);//eventsMap是object
測試程式碼3:
$("div").on("click", "p", function(){
// 這裡的this指向觸發點選事件的p元素(Element)
alert( $(this).text() );
});
這種呼叫邏輯的時候:第一個引數是types,第二個引數是selector,第三個引數是fn
returnFalse函式:
function returnFalse() {
                 return false;
                   }
測試程式碼4:
<input id="btn" type="button" value="點選" />
$("#btn").bind("click", function(){
alert("我是前面已經繫結的事件!");
});
// 只有第一次點選時,執行該事件處理函式
// 執行後one()會立即移除繫結的事件處理函式
$("#btn").one("click", function(){
alert("只彈出一次提示框!");
});
測試程式碼5:
$(function()
{
alert(jQuery.guid);//列印1
$("input").bind("click",function()
{
  alert("點選!");
})
alert(jQuery.guid);//列印2
})
jQuery中為每一個函式設定一個全域性的guid屬性,該guid用於事件模組和快取模組
測試uuid和guid:
<html>
<head>
<script type="text/javascript" src="/jquery/jquery.js"></script>
<script type="text/javascript">
$(function()
{
alert(jQuery.guid);//預設初始值是1

$("input").bind("click",function()
{
  alert("點選!");
})
alert(jQuery.guid);//事件處理或者快取系統會把guid++,所以是2
function T()
{
 alert("我測試你在不");
}
$("input").bind("click",T)
alert(jQuery.guid);//再次繫結事件以後guid++變成3,所以全域性jQuery.guid是不斷自增的!
alert(T.guid);//但是對於這個T函式來說,他是第二個函式,所以他的guid只是2
//下面為button新增資料測試uuid
$("button").data("sex","male");
alert("data新增資料測試uuid->"+jQuery.uuid);//uuid預設是0,採取++index前增長策略
alert("data新增資料測試jQuery.expando->"+jQuery.expando);
alert("data新增資料測試id->"+$("input")[0][jQuery.expando]);//記住這裡是DOM物件!
/*Query.data( elem, name, data, pvt)  
  elem[ internalKey ] = id = ++jQuery.uuid;  
jQuery.event.add: function( elem, types, handler, data, selector )  
  handler.guid = jQuery.guid++;
(每次繫結一個事件把事件處理函式的guid賦值,賦值為全域性的guid的值。但是每次繫結guid自增,所以函式用到的只是前面的一個guid,這主要來源於guid++是賦值後增長的!所以T.guid===2不是3)
})
</script>
</head>
<body>
<p>段落文字內容
<input type="button" value="點選" />    
</p>
</body>
</html>
每次繫結事件都會把guid自增,每次儲存資料會把uuid自增!
程式碼片段分析:‘
fn = function( event ) {
// Can use an empty set, since event contains the info
jQuery().off( event );
return origFn.apply( this, arguments );
};
對於fn來說,新增事件以後傳入該函式一個[object MouseEvent]物件,他做的事情有兩件:(1)把這個MouseEvent事件通過jQuery().off移除
(2)把上次的one函式傳遞進來的函式通過閉包引用並且執行!
  例項程式碼:(第一次點選彈出兩個對話方塊,以後彈出一次!,因為click事件已經被移除了!)
  <input id="btn" type="button" value="點選" />
  $("#btn").bind("click", function(){
alert("我是前面已經繫結的事件!");
});
// 只有第一次點選時,執行該事件處理函式·1
// 執行後one()會立即移除繫結的事件處理函式
$("#btn").one("click", function(){
alert("只彈出一次提示框!");
});
下面開始on方法的原始碼分析:
on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
var type, origFn;
// Types can be a map of types/handlers
//如果第一個types的物件是object型別,見程式碼1
if ( typeof types === "object" ) {
// ( types-Object, selector, data )
//如果第二個引數不是string,那麼呼叫方式就是$("p").on(typeObject,dataObject)也就是第一個引數是type物件,第二個引數是資料物件
if ( typeof selector !== "string" ) {
// ( types-Object, data )
data = data || selector;
selector = undefined;
}
//獲取types封裝的每一個type物件
for ( type in types ) {
//繼續逐個繫結到選擇器選擇的物件上面去。也就是雖然事件型別是一個object物件,但是還是要逐個繫結事件
//type是"mouseenter",但是types[type]就是function物件
this.on( type, selector, data, types[ type ], one );
}
return this;
}
 //如果第一個引數不是object型別,同時第三個和第四個引數data和fn同時是null,因為undefined==null恆成立!
 //這個if語句的呼叫邏輯是:$("p").on("click",function(){})也就是隻有兩個引數,這種呼叫方式第二個引數是fn
 //第一個是types,data和selector是undefined。on: function( types, selector, data, fn, /*INTERNAL*/ one )
if ( data == null && fn == null ) {
// ( types, fn )
//把selector賦值給fn,記住韋恩圖的邏輯!
fn = selector;
data = selector = undefined;
} else if ( fn == null ) {//僅僅fn為null,data不是null,function( types, selector, data, fn, /*INTERNAL*/ one )
if ( typeof selector === "string" ) {
// ( types, selector, fn )
fn = data;
data = undefined;
} else {
// ( types, data, fn )呼叫方式
//function( types, selector, data, fn, /*INTERNAL*/ one )
fn = data;
data = selector;
selector = undefined;
}
}
//如果第四個引數是false,那麼就把事件觸發時候的回撥函式設為returnFalse函式!
if ( fn === false ) {
fn = returnFalse;
} else if ( !fn ) {
return this;
}
//如果傳入了最後一個引數,同時設定為1,在下面的one()函式中會被呼叫
if ( one === 1 ) {
//儲存原來的傳遞過來的事件函式
//把one呼叫的函式作為區域性變數儲存下來
origFn = fn;
//對傳進來的fn重新賦值,這個函式是一個全新的函式,這個函式首先移除相應的事件,移除以後還要做一件事就是把one傳遞進來的函式執行
//一次
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
//設定新函式的guid,如果one傳進來的函式已經有了guid,那麼直接賦值,否則從jQuery獲取
fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
}
//jQuery.event.add傳入引數,第一個引數是遍歷出來的jQuery物件轉化成的DOM物件,即$("p")[i]
//第二個引數是型別,第三個引數回撥函式,第四個引數是傳遞的額外資料,最後一個是選擇器
return this.each( function() {
//把這個新函式新增到相應的this物件上去,this是jQuery物件
jQuery.event.add( this, types, fn, data, selector );
});
},
總結:

on方法其實就是對每一個呼叫物件單獨呼叫jQuery.event.add方法進行事件繫結!但是因為one方法也是呼叫的是on方法,所以最後一個引數one就是表示是否是one方法,如果是,該方法就是首先把該事件移除,同時為add方法傳入的新的函式控制代碼,該新的函式通過閉包的形式訪問原始的函式!