jQuery源碼學習(二)

有很長一段時間沒有更新博客了,近一段時間開始重新梳理知識點和寫博客了。重要的事情說三遍,新的博客地址:歡迎訪問,新的博客地址:歡迎訪問,新的博客地址:歡迎訪問

回調對象Callbacks

回調對象Callbacks就是用來管理回調函數隊列的。
###參數說明###
它提供幾個便捷的處理參數

  • once: 確保這個回調列表只執行一次
  • memory: 保持以前的值,將添加到這個列表的後面的最新的值立即執行調用任何回調
  • unique: 確保一次只能添加一個回調(所以在列表中沒有重複的回調).
  • stopOnFalse: 當一個回調返回false 時中斷調用
    once和stopOnFalse作用於fire
    memory和unique作用於add
    once在源碼中的實現:
// 以給定的上下文和參數調用所有回調函數
fireWith: function( context, args ) {
//第二次觸發的時候fired爲true
//stack = !options.once && []
//!fired爲false
//如果沒有設置once,!options.once && []爲true  則再次觸發fire( args );
//如果設置了once,!options.once && []爲false  則不會再次觸發fire( args );
				if ( list && ( !fired || stack ) ) {
					args = args || [];
					//args.slice是判斷args是不是數組
					args = [ context, args.slice ? args.slice() : args ];
					if ( firing ) {//如果正在回調
						//將參數推入堆棧,等待當前回調結束再調用
						stack.push( args );
					} else {//否則直接調用
						fire( args );
					}
				}
				return this;
			}

memory在源碼中的實現:

// 存在memory就會再次觸發fire
else if ( memory ) {
//如果options.memory爲true,則將memory做爲參數,應用最近增加的回調函數
		firingStart = start;
		fire( memory );
					}

unique在源碼中的實現:
options.unique->如果沒有設置unique,不管list中有沒有該回調都可以添加
options.unique->如果設置了unique,只有list中沒有纔可以添加該回調

if ( !options.unique || !self.has( arg ) ) {//確保是否可以重複
		list.push( arg );
	}

stopOnFalse在源碼中的實現:

//data[ 0 ]是執行環境
//data[ 1 ]就是fire中的參數
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
	//阻止未來可能由於add所產生的回調
	memory = false; 
	break;//由於options.stopOnFalse設置爲true,所以當有回調函數返回值爲false時退出循環
				}

once_demo:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<title>once</title>
</head>
<body>
	<script type="text/javascript" src="../jquery-2.1.1.js"></script>
	<script type="text/javascript">
		var cb = $.Callbacks("once");
		function aaa(){
			console.log(1);
		}
		function bbb(){
			console.log(2);
		}
		cb.add(aaa);
		cb.add(bbb);
		cb.fire();//輸出1
		cb.fire();//不執行
	</script>
</body>
</html>

memory_demo:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<title>memory</title>
</head>
<body>
	<script type="text/javascript" src="../jquery-2.1.1.js"></script>
	<script type="text/javascript">
		var cb = $.Callbacks("memory");
		function aaa(){
			console.log(1);
		}
		function bbb(){
			console.log(2);
		}
		cb.add(aaa);
		cb.fire();
		cb.add(bbb);//由於memory有記憶功能,後添加的也能觸發
	</script>
</body>
</html>

unique_demo:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<title>Unique</title>
</head>
<body>
	<script type="text/javascript" src="../jquery-2.1.1.js"></script>
	<script type="text/javascript">
		var cb = $.Callbacks("unique");
		function aaa(){
			console.log(1);
		}
		cb.add(aaa);//有效
		cb.add(aaa);//添加不進去
		cb.fire();//輸出 1
	</script>
</body>
</html>

源碼處理解析:

if ( !options.unique || !self.has( arg ) ) { //確保是否可以重複
    list.push( arg );
}                

如果options.unique爲true,並且回調列表中不包含arg,則將arg添加到回調列表中
stopOnFalse_demo:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<title>stopOnFalse</title>
</head>
<body>
	<script type="text/javascript" src="../jquery-2.1.1.js"></script>
	<script type="text/javascript">
		var cb = $.Callbacks("stopOnFalse");
		function aaa(){
			console.log(1);
			return false;
		}
		function bbb(){
			console.log(2);
		}
		cb.add(aaa);//執行輸出1
		cb.add(bbb);//不執行
		cb.fire();
	</script>
</body>
</html>

混合參數_demo:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<title>混合參數</title>
</head>
<body>
	<script type="text/javascript" src="../jquery-2.1.1.js"></script>
	<script type="text/javascript">
		var cb = $.Callbacks("once memory");
		function aaa(){
			console.log(1);
		}
		function bbb(){
			console.log(2);
		}
		cb.add(aaa);
		cb.fire();//執行輸出1 2
		cb.add(bbb);
		cb.fire();//因爲once,不執行
	</script>
</body>
</html>

###方法介紹 ##
根據jQuery.Callbacks()的API:

  • callbacks.add() 回調列表中添加一個回調或回調的集合。
  • callbacks.disable() 禁用回調列表中的回調
  • callbacks.disabled() 確定回調列表是否已被禁用。
  • callbacks.empty() 從列表中刪除所有的回調.
  • callbacks.fire() 用給定的參數調用所有的回調
  • callbacks.fired() 訪問給定的上下文和參數列表中的所有回調。
  • callbacks.fireWith() 訪問給定的上下文和參數列表中的所有回調。
  • callbacks.has() 確定列表中是否提供一個回調
  • callbacks.lock() 鎖定當前狀態的回調列表。
  • callbacks.locked() 確定回調列表是否已被鎖定。
  • callbacks.remove() 從回調列表中的刪除一個回調或回調集合。

延遲對象Deferred

延遲對象是基於回調對象來實現的,用來實現異步的統一管理
when()是用來輔助延遲對象的
大體框架如下:

jQuery.extend({//兩個工具方法
			Deferred:function(){},
			when : function(){}
		});

###$.Deferred()###
有三組對應關係:

  • resolve->done
  • reject->fail
  • notify->progress

前兩組對應關係,分別對應相應的狀態變化:

  • pending->resolved
  • pending->rejected
    一旦發生兩種狀態變化中的一種,則狀態變爲不可變
<body>
	<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
	<script type="text/javascript">
	var cb = $.Callbacks();
	setTimeout(function(){
		alert(111);
		cb.fire();
	},1000);
	//add先把這個函數存到數組裏
	//當調用fire的時候再觸發
	cb.add(function(){
		alert(222);
	});
	</script>
	<script type="text/javascript">
	//延遲對象-實現對異步的統一管理
	var dfd = $.Deferred();//延遲對象
	setTimeout(function(){
		alert(111);
		dfd.resolve();
	},1000);
	dfd.done(function(){
		alert(222);
	});
	</script>
	<script type="text/javascript">
	setTimeout(function(){
		alert(111);
	},1000);
	//定時器就是異步的
	//延遲對象可以實現從上往下的順序
	//先彈出1,再彈出2
	alert(222);
	</script>
	<script type="text/javascript">
	var dfd = $.Deferred();
	setTimeout(function(){
		alert(111);
		dfd.reject();
	},1000);
	dfd.fail(function(){
		alert(222);
	});
	</script>
	<script type="text/javascript">
	var dfd = $.Deferred();
	setTimeout(function(){
		alert(111);
		dfd.notify();
	},1000);
	dfd.progress(function(){
		alert(222);
	});
	</script>
</body>
<body>
	<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
	<script type="text/javascript">
	var def = $.Deferred();
	setInterval(function(){
		alert(111);
		def.resolve();
		// def.reject();
		// def.notify();
	},1000);
	def.done(function(){
		alert("成功");//只會彈一次成功  
		//因爲源碼中設置了jQuery.Callbacks("once memory")
	}).fail(function(){
		alert("失敗");//只會彈一次失敗  
		//因爲源碼中設置了jQuery.Callbacks("once memory")
	}).progress(function(){
		alert("進度中");//每隔一秒都會彈出
	});
	</script>
</body>

###deferred和promise的區別###
Deferred對象和promise對象都是延遲對象
####Deferred####

  • resolve
  • reject
  • notify
  • state
  • always
  • then
  • promise
  • pipe
  • done
  • fail
  • progress
    ####promise對象####
  • state
  • always
  • then
  • promise
  • pipe
  • done
  • fail
  • progress

通過上面的列舉,我們可以看出:deferred比promise多了resolve、reject和notify

<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
function aaa(){
	var dfd = $.Deferred();
	setTimeout(function(){
		dfd.resolve();//不會觸發
	},1000);
	return dfd;
}
var newDfd = aaa();
newDfd.done(function(){
	alert("成功");
}).fail(function(){
	alert("失敗");//彈出失敗
});
newDfd.reject();//會先觸發
</script>
</body>

dfd.promise():延遲對象調用promise()方法,返回一個promise對象
promise對象中沒有reject()方法
這裏爲了阻止延遲對象的狀態在外部被改變,需要這樣來調用dfd.promise()。它的作用是,在原來的deferred對象上返回promise對象,後者只開放與改變執行狀態無關的方法(比如done()方法和fail()方法),屏蔽與改變執行狀態有關的方法(比如resolve()方法和reject()方法),從而使得執行狀態不能被改變。
源碼中是這樣實現的:
dfd.promise()這裏參數爲空,所以這裏就返回promise對象

promise: function( obj ) {
	return obj != null ? jQuery.extend( obj, promise ) : promise;
				}
<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
function aaa(){
	var dfd = $.Deferred();
	setTimeout(function(){
		dfd.resolve();
	},1000);
	return dfd.promise();
}
var newDfd = aaa();
newDfd.done(function(){
	alert("成功");//彈出成功
}).fail(function(){
	alert("失敗");
});
newDfd.reject();//會報錯  promise對象沒有改變狀態的方法
</script>
</body>

###always###

<body>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
        var dfd = $.Deferred();
        setTimeout(function(){
            //dfd.resolve();
            dfd.reject();
        },1000);
        //不管是resolve還是reject,always總會執行
        dfd.always(function(){
            alert("總會彈出");
        });
    </script>
</body>

源碼中是這樣寫的:

always: function() {
//總會觸發是因爲既有done又有fail
deferred.done( arguments ).fail( arguments );
			return this;
			//return this表示可以進行鏈式操作
				}

###state###
返回狀態
resovled或者rejected

<body>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
    function aaa(){
        var dfd=$.Deferred();
        alert(dfd.state());//pending
        setTimeout(function(){
            dfd.resolve();//resolve()之後,狀態變爲resolved
            alert(dfd.state());//resolved
        },1000);
        return dfd;
    }
    var newDfd=aaa();
    newDfd.done(function(){
        alert('成功');
    }).fail(function(){
        alert('失敗');
    });
</script>
</body>

###then###

<body>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
        var dfd = $.Deferred();
        setTimeout(function(){
            //dfd.resolve();
            dfd.reject();
        },1000);
        //then中對應三種狀態
        dfd.then(function(){
            alert("成功");
        },function(){
            alert("失敗");
        },function(){
            alert("進行中");
        });
    </script>
</body>

###pipe###

// then和pipe代碼一樣,功能不一樣
// pipe可以把延遲對象變長
promise.pipe = promise.then;
<body>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
        var dfd = $.Deferred();
        setTimeout(function(){
            //dfd.resolve();
            dfd.resolve('hello');//可以接收參數傳遞
        },1000);
        var newDfd = dfd.pipe(function(){
            return arguments[0]+'你好';
        });
        newDfd.done(function(){
            alert(arguments[0]);//hello你好
        });
    </script>
</body>

###$.when()###
when是用來輔助延遲對象的工具方法

<body>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
        function aaa(){
            var dfd = $.Deferred();
            //dfd.resolve();
            dfd.reject();
            return dfd;
        }
        function bbb(){
            var dfd = $.Deferred();
            dfd.resolve();
            //dfd.reject();
            return dfd;
        }
        /*
        這樣寫只有aaa()和bbb()同時完成觸發才彈出,但是任意一個未完成就可以觸發fail()
         */
        //when是針對多個延遲對象的
        $.when(aaa(),bbb()).done(function(){
            alert('成功');
        }).fail(function(){
            alert('失敗');
        });
    </script>
</body>
<body>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
        function aaa(){
            var dfd = $.Deferred();
            dfd.resolve();
            return dfd;
        }
        function bbb(){
            var dfd = $.Deferred();
            dfd.reject();
            return dfd;  //這裏註銷後將返回成功,不註銷返回失敗
        }
        /*
        這樣寫只有aaa()和bbb()同時完成觸發才彈出,但是任意一個未完成就可以觸發fail()
         */
        //when是針對多個延遲對象的
        //when中的參數必須是延遲對象才起作用
        //如果不是延遲對象就會自動跳過該參數
        $.when(aaa(),bbb()).done(function(){
            alert('成功');
        }).fail(function(){
            alert('失敗');
        });
    </script>
</body>
	$.when(aaa(),bbb(),ccc(),ddd()).done(function(){
            alert('成功');
        });
        
        aaa()->arguments[0] done()
        bbb()->arguments[1] done()
        ccc()->arguments[2] done()
        ddd()->arguments[3] done()

when是針對多個延遲對象的,比如這裏針對四個延遲對象,when的源碼中有一個計數器,這裏計數器就是4,觸發一個延遲對象,計數器減1,當計數器爲0時。$.when()會返回一個延遲對象deferred.promise(),然後觸發resolve()方法,最後觸發done。

相關閱讀博文:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章