jQuery鏈?zhǔn)讲僮魅绾螌?shí)現(xiàn)以及為什么要用鏈?zhǔn)讲僮?/h1>
更新時(shí)間:2013年01月17日 14:19:23 投稿:whsnow
jQuery的鏈?zhǔn)讲僮魇侨绾螌?shí)現(xiàn)的?為什么要用鏈?zhǔn)讲僮??接下?lái)會(huì)幫助大家解決這兩個(gè)問(wèn)題,感興趣的朋友可以了解下
兩個(gè)問(wèn)題
1.jQuery的鏈?zhǔn)讲僮魇侨绾螌?shí)現(xiàn)的?
2.為什么要用鏈?zhǔn)讲僮鳎?/strong>
大家認(rèn)為這兩個(gè)問(wèn)題哪個(gè)好回答一點(diǎn)呢?
鏈?zhǔn)讲僮?/strong>
原理相信百度一下一大把,實(shí)際上鏈?zhǔn)讲僮鲀H僅是通過(guò)對(duì)象上的方法最后
return this
把對(duì)象再返回來(lái),對(duì)象當(dāng)然可以繼續(xù)調(diào)用方法啦,所以就可以鏈?zhǔn)讲僮髁?。那么?strong>簡(jiǎn)單實(shí)現(xiàn)一個(gè):
復(fù)制代碼 代碼如下:
//定義一個(gè)JS類
function Demo() {
}
//擴(kuò)展它的prototype
Demo.prototype ={
setName:function (name) {
this.name = name;
return this;
},
getName:function () {
return this.name;
},
setAge:function (age) {
this.age = age;
return this;
}
};
////工廠函數(shù)
function D() {
return new Demo();
}
//去實(shí)現(xiàn)可鏈?zhǔn)降恼{(diào)用
D().setName("CJ").setAge(18).setName();
但……為什么要用呢?
一般的解釋:
節(jié)省代碼量,代碼看起來(lái)更優(yōu)雅。
例如如果沒(méi)有鏈?zhǔn)剑敲茨憧赡苄枰@樣寫(xiě)代碼:
復(fù)制代碼 代碼如下:
document.getElementById("ele").dosomething();
document.getElementById("ele").dootherthing();
這個(gè)代碼中調(diào)用了兩次document.getElementById來(lái)獲取DOM樹(shù)的元素,這樣消耗比較大,而且要寫(xiě)兩行,而鏈?zhǔn)街灰獙?xiě)一行,節(jié)省了代碼……
但我們也可以用緩存元素啊。比如:
復(fù)制代碼 代碼如下:
var ele = document.getElementById("ele");
ele.dosomething();
ele.dootherthing();
而且兩行并沒(méi)有比一行多多少代碼,甚至相應(yīng)的封裝反而使得代碼更多了。
最糟糕的是所有對(duì)象的方法返回的都是對(duì)象本身,也就是說(shuō)沒(méi)有返回值,這不一定在任何環(huán)境下都適合。
舉個(gè)例子,我們想弄一個(gè)超大整數(shù)BigInteger(意思是如果用Javascript的Number保存可能會(huì)溢出的整數(shù)),順便擴(kuò)展他的運(yùn)算方法,會(huì)適合用鏈?zhǔn)讲僮髅矗?br />
例如運(yùn)算31415926535 * 4 - 271828182,如果設(shè)計(jì)成鏈?zhǔn)斤L(fēng)格的方法可能會(huì)是這樣的:
復(fù)制代碼 代碼如下:
var result = (new BigInteger("31415926535")).multiply(new BigInteger("4")).subtract(new BigInteger("271828182")).val();
console.log("result == " + result);
這看起來(lái)似乎也很優(yōu)雅,但是如果我們想要中間的結(jié)果怎么辦呢?或許會(huì)寫(xiě)成這樣:
復(fù)制代碼 代碼如下:
var bigInteger = new BigInteger("31415926535");
var result1 = bigInteger.multiply(new BigInteger("4")).val();
var result2 = bigInteger.subtract(new BigInteger("271828182")).val();
console.log("result1 == " + result1 + ", result2 == " + result2);
這似乎一點(diǎn)也不優(yōu)雅了,和不用鏈?zhǔn)讲僮鳑](méi)啥不同嘛!
那么如果要求是原來(lái)的BigInteger不能改變呢?好吧,鏈?zhǔn)讲僮魉坪醪荒軡M足這個(gè)需求了。
那么到底為什么要用鏈?zhǔn)讲僮髂兀?/strong>
為了更好的異步體驗(yàn)
Javascript是無(wú)阻塞語(yǔ)言,所以他不是沒(méi)阻塞,而是不能阻塞,所以他需要通過(guò)事件來(lái)驅(qū)動(dòng),異步來(lái)完成一些本需要阻塞進(jìn)程的操作。
但是異步編程是一種令人瘋狂的東西……運(yùn)行時(shí)候是分離的倒不要緊,但是編寫(xiě)代碼時(shí)候也是分離的就……
常見(jiàn)的異步編程模型有哪些呢?
•回調(diào)函數(shù)
所謂的回調(diào)函數(shù),意指先在系統(tǒng)的某個(gè)地方對(duì)函數(shù)進(jìn)行注冊(cè),讓系統(tǒng)知道這個(gè)函數(shù)的存在,然后在以后,當(dāng)某個(gè)事件發(fā)生時(shí),再調(diào)用這個(gè)函數(shù)對(duì)事件進(jìn)行響應(yīng)。
復(fù)制代碼 代碼如下:
function f(num, callback){
if(num<0) {
alert("調(diào)用低層函數(shù)處理!");
alert("分?jǐn)?shù)不能為負(fù),輸入錯(cuò)誤!");
}else if(num==0){
alert("調(diào)用低層函數(shù)處理!");
alert("該學(xué)生可能未參加考試!");
}else{
alert("調(diào)用高層函數(shù)處理!");
setTimeout(function(){callback();}, 1000);
}
}
這里callback則是回調(diào)函數(shù)。可以發(fā)現(xiàn)只有當(dāng)num為非負(fù)數(shù)時(shí)候callback才會(huì)調(diào)用。
但是問(wèn)題,如果我們不看函數(shù)內(nèi)部,我們并不知道callback會(huì)幾時(shí)調(diào)用,在什么情況下調(diào)用,代碼間產(chǎn)生了一定耦合,流程上也會(huì)產(chǎn)生一定的混亂。
雖然回調(diào)函數(shù)是一種簡(jiǎn)單而易于部署的實(shí)現(xiàn)異步的方法,但從編程體驗(yàn)來(lái)說(shuō)它卻不夠好。
•事件
也就是采用事件驅(qū)動(dòng),執(zhí)行順序取決于事件順序。
復(fù)制代碼 代碼如下:
function EventTarget(){
this.handlers = {};
}
EventTarget.prototype = {
constructor: EventTarget,
addHandler: function(type, handler){
this.handlers[type] = [];
},
fire: function(){
if(!event.target){
event.target = this;
}
if(this.handlers[event.type instanceof Array]){
var handlers = this.handlers[event.type];
for(var i = 0, len = handlers.length, i < len; i++){
handlers[i](event);
}
}
},
removeHandler: function(type, handler){
if(this.handlers[type] instanceof Array){
var handlers = this.handlers[type];
for(var i = 0, le = handlers.length; i < len; i++){
if(handlers[i] === handler){
break;
}
}
handlers.splice(i, 1);
}
}
};
上面是《JavaScript高級(jí)程序設(shè)計(jì)》中的自定義事件實(shí)現(xiàn)。于是我們就可以通過(guò)addHandler來(lái)綁定事件處理函數(shù),用fire來(lái)觸發(fā)事件,用removeHandler來(lái)刪除事件處理函數(shù)。
雖然通過(guò)事件解耦了,但流程順序更加混亂了。
•鏈?zhǔn)疆惒?/strong>
個(gè)人覺(jué)得鏈?zhǔn)讲僮髯钪档梅Q贊的還是其解決了異步編程模型的執(zhí)行流程不清晰的問(wèn)題。jQuery中$(document).ready就非常好的闡釋了這一理念。DOMCotentLoaded是一個(gè)事件,在DOM并未加載前,jQuery的大部分操作都不會(huì)奏效,但jQuery的設(shè)計(jì)者并沒(méi)有把他當(dāng)成事件一樣來(lái)處理,而是轉(zhuǎn)成一種“選其對(duì)象,對(duì)其操作”的思路。$選擇了document對(duì)象,ready是其方法進(jìn)行操作。這樣子流程問(wèn)題就非常清晰了,在鏈條越后位置的方法就越后執(zhí)行。
復(fù)制代碼 代碼如下:
(function(){
var isReady=false; //判斷onDOMReady方法是否已經(jīng)被執(zhí)行過(guò)
var readyList= [];//把需要執(zhí)行的方法先暫存在這個(gè)數(shù)組里
var timer;//定時(shí)器句柄
ready=function(fn) {
if (isReady )
fn.call( document);
else
readyList.push( function() { return fn.call(this);});
return this;
}
var onDOMReady=function(){
for(var i=0;i<readyList.length;i++){
readyList[i].apply(document);
}
readyList = null;
}
var bindReady = function(evt){
if(isReady) return;
isReady=true;
onDOMReady.call(window);
if(document.removeEventListener){
document.removeEventListener("DOMContentLoaded", bindReady, false);
}else if(document.attachEvent){
document.detachEvent("onreadystatechange", bindReady);
if(window == window.top){
clearInterval(timer);
timer = null;
}
}
};
if(document.addEventListener){
document.addEventListener("DOMContentLoaded", bindReady, false);
}else if(document.attachEvent){
document.attachEvent("onreadystatechange", function(){
if((/loaded|complete/).test(document.readyState))
bindReady();
});
if(window == window.top){
timer = setInterval(function(){
try{
isReady||document.documentElement.doScroll('left');//在IE下用能否執(zhí)行doScroll判斷dom是否加載完畢
}catch(e){
return;
}
bindReady();
},5);
}
}
})();
上面的代碼不能用$(document).ready,而應(yīng)該是window.ready。
•Promise
CommonJS中的異步編程模型也延續(xù)了這一想法,每一個(gè)異步任務(wù)返回一個(gè)Promise對(duì)象,該對(duì)象有一個(gè)then方法,允許指定回調(diào)函數(shù)。
所以我們可以這樣寫(xiě):
f1().then(f2).then(f3);
這種方法我們無(wú)需太過(guò)關(guān)注實(shí)現(xiàn),也不太需要理解異步,只要懂得通過(guò)函數(shù)選對(duì)象,通過(guò)then進(jìn)行操作,就能進(jìn)行異步編程。
相關(guān)文章
-
jquery+css3打造一款ajax分頁(yè)插件(自寫(xiě))
這篇文章主要介紹了自己寫(xiě)的一款ajax分頁(yè)插件,用jquery+css3打造支持IE6+,但沒(méi)有動(dòng)畫(huà)效果,需要的朋友可以參考下 2014-06-06
-
jquery實(shí)現(xiàn)自適應(yīng)banner焦點(diǎn)圖
本文主要分享了jquery實(shí)現(xiàn)自適應(yīng)banner焦點(diǎn)圖的示例代碼,具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧 2017-02-02
-
jQuery實(shí)現(xiàn)指定內(nèi)容滾動(dòng)同時(shí)左側(cè)或其它地方不滾動(dòng)的方法
這篇文章主要介紹了jQuery實(shí)現(xiàn)指定內(nèi)容滾動(dòng)同時(shí)左側(cè)或其它地方不滾動(dòng)的方法,可實(shí)現(xiàn)頁(yè)面滾動(dòng)的時(shí)候指定位置懸浮固定的效果,涉及jquery針對(duì)頁(yè)面元素屬性的動(dòng)態(tài)操作技巧,需要的朋友可以參考下 2015-08-08
-
JQuery在循環(huán)中綁定事件的問(wèn)題詳解
下面小編就為大家?guī)?lái)一篇JQuery在循環(huán)中綁定事件的問(wèn)題詳解。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧 2016-06-06
-
jQuery中(function($){})(jQuery)詳解
本文通過(guò)具體示例向大家詳細(xì)介紹了jQuery中的(function($){})(jQuery)的用法和意義,對(duì)此有相同困惑的小伙伴可以參考下本文。 2015-07-07
-
獲取select的value、text值的簡(jiǎn)單示例(jquery與javascript)
本篇文章主要分享了獲取select的value、text值的簡(jiǎn)單示例代碼,代碼簡(jiǎn)潔明了,有需要的朋友可以參考下 2016-12-12
最新評(píng)論
兩個(gè)問(wèn)題
1.jQuery的鏈?zhǔn)讲僮魇侨绾螌?shí)現(xiàn)的?
2.為什么要用鏈?zhǔn)讲僮鳎?/strong>
大家認(rèn)為這兩個(gè)問(wèn)題哪個(gè)好回答一點(diǎn)呢?
鏈?zhǔn)讲僮?/strong>
原理相信百度一下一大把,實(shí)際上鏈?zhǔn)讲僮鲀H僅是通過(guò)對(duì)象上的方法最后
return this
把對(duì)象再返回來(lái),對(duì)象當(dāng)然可以繼續(xù)調(diào)用方法啦,所以就可以鏈?zhǔn)讲僮髁?。那么?strong>簡(jiǎn)單實(shí)現(xiàn)一個(gè):
//定義一個(gè)JS類
function Demo() {
}
//擴(kuò)展它的prototype
Demo.prototype ={
setName:function (name) {
this.name = name;
return this;
},
getName:function () {
return this.name;
},
setAge:function (age) {
this.age = age;
return this;
}
};
////工廠函數(shù)
function D() {
return new Demo();
}
//去實(shí)現(xiàn)可鏈?zhǔn)降恼{(diào)用
D().setName("CJ").setAge(18).setName();
但……為什么要用呢?
一般的解釋:
節(jié)省代碼量,代碼看起來(lái)更優(yōu)雅。
例如如果沒(méi)有鏈?zhǔn)剑敲茨憧赡苄枰@樣寫(xiě)代碼:
document.getElementById("ele").dosomething();
document.getElementById("ele").dootherthing();
這個(gè)代碼中調(diào)用了兩次document.getElementById來(lái)獲取DOM樹(shù)的元素,這樣消耗比較大,而且要寫(xiě)兩行,而鏈?zhǔn)街灰獙?xiě)一行,節(jié)省了代碼……
但我們也可以用緩存元素啊。比如:
var ele = document.getElementById("ele");
ele.dosomething();
ele.dootherthing();
而且兩行并沒(méi)有比一行多多少代碼,甚至相應(yīng)的封裝反而使得代碼更多了。
最糟糕的是所有對(duì)象的方法返回的都是對(duì)象本身,也就是說(shuō)沒(méi)有返回值,這不一定在任何環(huán)境下都適合。
舉個(gè)例子,我們想弄一個(gè)超大整數(shù)BigInteger(意思是如果用Javascript的Number保存可能會(huì)溢出的整數(shù)),順便擴(kuò)展他的運(yùn)算方法,會(huì)適合用鏈?zhǔn)讲僮髅矗?br />
例如運(yùn)算31415926535 * 4 - 271828182,如果設(shè)計(jì)成鏈?zhǔn)斤L(fēng)格的方法可能會(huì)是這樣的:
var result = (new BigInteger("31415926535")).multiply(new BigInteger("4")).subtract(new BigInteger("271828182")).val();
console.log("result == " + result);
這看起來(lái)似乎也很優(yōu)雅,但是如果我們想要中間的結(jié)果怎么辦呢?或許會(huì)寫(xiě)成這樣:
var bigInteger = new BigInteger("31415926535");
var result1 = bigInteger.multiply(new BigInteger("4")).val();
var result2 = bigInteger.subtract(new BigInteger("271828182")).val();
console.log("result1 == " + result1 + ", result2 == " + result2);
這似乎一點(diǎn)也不優(yōu)雅了,和不用鏈?zhǔn)讲僮鳑](méi)啥不同嘛!
那么如果要求是原來(lái)的BigInteger不能改變呢?好吧,鏈?zhǔn)讲僮魉坪醪荒軡M足這個(gè)需求了。
那么到底為什么要用鏈?zhǔn)讲僮髂兀?/strong>
為了更好的異步體驗(yàn)
Javascript是無(wú)阻塞語(yǔ)言,所以他不是沒(méi)阻塞,而是不能阻塞,所以他需要通過(guò)事件來(lái)驅(qū)動(dòng),異步來(lái)完成一些本需要阻塞進(jìn)程的操作。
但是異步編程是一種令人瘋狂的東西……運(yùn)行時(shí)候是分離的倒不要緊,但是編寫(xiě)代碼時(shí)候也是分離的就……
常見(jiàn)的異步編程模型有哪些呢?
•回調(diào)函數(shù)
所謂的回調(diào)函數(shù),意指先在系統(tǒng)的某個(gè)地方對(duì)函數(shù)進(jìn)行注冊(cè),讓系統(tǒng)知道這個(gè)函數(shù)的存在,然后在以后,當(dāng)某個(gè)事件發(fā)生時(shí),再調(diào)用這個(gè)函數(shù)對(duì)事件進(jìn)行響應(yīng)。
function f(num, callback){
if(num<0) {
alert("調(diào)用低層函數(shù)處理!");
alert("分?jǐn)?shù)不能為負(fù),輸入錯(cuò)誤!");
}else if(num==0){
alert("調(diào)用低層函數(shù)處理!");
alert("該學(xué)生可能未參加考試!");
}else{
alert("調(diào)用高層函數(shù)處理!");
setTimeout(function(){callback();}, 1000);
}
}
這里callback則是回調(diào)函數(shù)。可以發(fā)現(xiàn)只有當(dāng)num為非負(fù)數(shù)時(shí)候callback才會(huì)調(diào)用。
但是問(wèn)題,如果我們不看函數(shù)內(nèi)部,我們并不知道callback會(huì)幾時(shí)調(diào)用,在什么情況下調(diào)用,代碼間產(chǎn)生了一定耦合,流程上也會(huì)產(chǎn)生一定的混亂。
雖然回調(diào)函數(shù)是一種簡(jiǎn)單而易于部署的實(shí)現(xiàn)異步的方法,但從編程體驗(yàn)來(lái)說(shuō)它卻不夠好。
•事件
也就是采用事件驅(qū)動(dòng),執(zhí)行順序取決于事件順序。
function EventTarget(){
this.handlers = {};
}
EventTarget.prototype = {
constructor: EventTarget,
addHandler: function(type, handler){
this.handlers[type] = [];
},
fire: function(){
if(!event.target){
event.target = this;
}
if(this.handlers[event.type instanceof Array]){
var handlers = this.handlers[event.type];
for(var i = 0, len = handlers.length, i < len; i++){
handlers[i](event);
}
}
},
removeHandler: function(type, handler){
if(this.handlers[type] instanceof Array){
var handlers = this.handlers[type];
for(var i = 0, le = handlers.length; i < len; i++){
if(handlers[i] === handler){
break;
}
}
handlers.splice(i, 1);
}
}
};
上面是《JavaScript高級(jí)程序設(shè)計(jì)》中的自定義事件實(shí)現(xiàn)。于是我們就可以通過(guò)addHandler來(lái)綁定事件處理函數(shù),用fire來(lái)觸發(fā)事件,用removeHandler來(lái)刪除事件處理函數(shù)。
雖然通過(guò)事件解耦了,但流程順序更加混亂了。
•鏈?zhǔn)疆惒?/strong>
個(gè)人覺(jué)得鏈?zhǔn)讲僮髯钪档梅Q贊的還是其解決了異步編程模型的執(zhí)行流程不清晰的問(wèn)題。jQuery中$(document).ready就非常好的闡釋了這一理念。DOMCotentLoaded是一個(gè)事件,在DOM并未加載前,jQuery的大部分操作都不會(huì)奏效,但jQuery的設(shè)計(jì)者并沒(méi)有把他當(dāng)成事件一樣來(lái)處理,而是轉(zhuǎn)成一種“選其對(duì)象,對(duì)其操作”的思路。$選擇了document對(duì)象,ready是其方法進(jìn)行操作。這樣子流程問(wèn)題就非常清晰了,在鏈條越后位置的方法就越后執(zhí)行。
(function(){
var isReady=false; //判斷onDOMReady方法是否已經(jīng)被執(zhí)行過(guò)
var readyList= [];//把需要執(zhí)行的方法先暫存在這個(gè)數(shù)組里
var timer;//定時(shí)器句柄
ready=function(fn) {
if (isReady )
fn.call( document);
else
readyList.push( function() { return fn.call(this);});
return this;
}
var onDOMReady=function(){
for(var i=0;i<readyList.length;i++){
readyList[i].apply(document);
}
readyList = null;
}
var bindReady = function(evt){
if(isReady) return;
isReady=true;
onDOMReady.call(window);
if(document.removeEventListener){
document.removeEventListener("DOMContentLoaded", bindReady, false);
}else if(document.attachEvent){
document.detachEvent("onreadystatechange", bindReady);
if(window == window.top){
clearInterval(timer);
timer = null;
}
}
};
if(document.addEventListener){
document.addEventListener("DOMContentLoaded", bindReady, false);
}else if(document.attachEvent){
document.attachEvent("onreadystatechange", function(){
if((/loaded|complete/).test(document.readyState))
bindReady();
});
if(window == window.top){
timer = setInterval(function(){
try{
isReady||document.documentElement.doScroll('left');//在IE下用能否執(zhí)行doScroll判斷dom是否加載完畢
}catch(e){
return;
}
bindReady();
},5);
}
}
})();
上面的代碼不能用$(document).ready,而應(yīng)該是window.ready。
•Promise
CommonJS中的異步編程模型也延續(xù)了這一想法,每一個(gè)異步任務(wù)返回一個(gè)Promise對(duì)象,該對(duì)象有一個(gè)then方法,允許指定回調(diào)函數(shù)。
所以我們可以這樣寫(xiě):
f1().then(f2).then(f3);
這種方法我們無(wú)需太過(guò)關(guān)注實(shí)現(xiàn),也不太需要理解異步,只要懂得通過(guò)函數(shù)選對(duì)象,通過(guò)then進(jìn)行操作,就能進(jìn)行異步編程。
相關(guān)文章
jquery+css3打造一款ajax分頁(yè)插件(自寫(xiě))
這篇文章主要介紹了自己寫(xiě)的一款ajax分頁(yè)插件,用jquery+css3打造支持IE6+,但沒(méi)有動(dòng)畫(huà)效果,需要的朋友可以參考下2014-06-06
jquery實(shí)現(xiàn)自適應(yīng)banner焦點(diǎn)圖
本文主要分享了jquery實(shí)現(xiàn)自適應(yīng)banner焦點(diǎn)圖的示例代碼,具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-02-02
jQuery實(shí)現(xiàn)指定內(nèi)容滾動(dòng)同時(shí)左側(cè)或其它地方不滾動(dòng)的方法
這篇文章主要介紹了jQuery實(shí)現(xiàn)指定內(nèi)容滾動(dòng)同時(shí)左側(cè)或其它地方不滾動(dòng)的方法,可實(shí)現(xiàn)頁(yè)面滾動(dòng)的時(shí)候指定位置懸浮固定的效果,涉及jquery針對(duì)頁(yè)面元素屬性的動(dòng)態(tài)操作技巧,需要的朋友可以參考下2015-08-08
JQuery在循環(huán)中綁定事件的問(wèn)題詳解
下面小編就為大家?guī)?lái)一篇JQuery在循環(huán)中綁定事件的問(wèn)題詳解。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-06-06
jQuery中(function($){})(jQuery)詳解
本文通過(guò)具體示例向大家詳細(xì)介紹了jQuery中的(function($){})(jQuery)的用法和意義,對(duì)此有相同困惑的小伙伴可以參考下本文。2015-07-07
獲取select的value、text值的簡(jiǎn)單示例(jquery與javascript)
本篇文章主要分享了獲取select的value、text值的簡(jiǎn)單示例代碼,代碼簡(jiǎn)潔明了,有需要的朋友可以參考下2016-12-12

