你是否曾經(jīng)見過像 $(".cta").click(function(){})這樣的JavaScrip代碼?或許你還會(huì)思考下 $('#X') 是什么,如果看到這些你都覺得摸不著頭腦,那請一定要讀完這篇文章。如果你覺得上述的代碼片段是不能正常工作的,那請先看一些jQuery的代碼范例,你會(huì)發(fā)現(xiàn)鏈接中的代碼都是這樣的結(jié)構(gòu)。
這篇文章將會(huì)分析下面代碼片段(動(dòng)畫化一個(gè)方形)中出現(xiàn)的一些關(guān)鍵知識點(diǎn)。你可能不會(huì)經(jīng)常接觸這樣的代碼,但了解一下這段代碼的機(jī)制有助于你理解jQuery:
$(document).ready(function(){
$("button").click(function(){
$("div").animate({height:"toggle"}).append("hi");
});
});
我們將會(huì)逐字逐句地解釋上述的代碼,告訴你JavaScript函數(shù)、jQuery對象還有事件驅(qū)動(dòng)編程的具體細(xì)節(jié)。希望看完這篇文章以后,再遇到神秘的jQuery代碼時(shí)你不會(huì)再頭疼。
$是什么?
在你第一眼看到$的時(shí)候,有一種高大上的猜測在你心中盤旋:這一定是個(gè)很特別很復(fù)雜的JS方法。事實(shí)上,它很普通,也沒有什么特殊的含義。$就是一個(gè)函數(shù),是jQuery函數(shù)的另一個(gè)名字罷了。
jQuery是一個(gè)比較簡單的JavaScript庫,它在瀏覽器兼容方面做得很好,而且還提供了許多很有用的特性用來操作網(wǎng)頁或者做些動(dòng)畫效果。你可以先引入jQuery庫的地址,然后就能使用jQuery函數(shù)(比如$)了:
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
或者你也可以直接在jQuery官網(wǎng)上下載它。
jQuery函數(shù)通常只需要一個(gè)參數(shù),這個(gè)參數(shù)可以是一個(gè)選擇器也可以是JS引用頁面上的內(nèi)容(比如document)。
選擇器就是CSS的一個(gè)片段,寫在{…}之前的內(nèi)容就是選擇器了。所以,$(“div”)和jQuery(“div”)是一個(gè)意思,就是簡單粗暴地把頁面上所有的<div>標(biāo)簽都選中,和在CSS中使用.div獲得的是同一個(gè)結(jié)果。
<style>
div {…}
</style>
還記得在代碼的最開頭有一個(gè)$(document)嗎?這一步是要把JS變量document傳入jQuery方法當(dāng)中。document是由瀏覽器來定義的,可以直接使用,它指的是文檔對象模型(DOM)的最頂層。DOM指的是各個(gè)瀏覽器是如何來解釋頁面的整個(gè)HTML結(jié)構(gòu)。用jQuery寫的程序是基于DOM的。jQuery中的$(‘div’)和document.getElementsByTagNae(“div”)得到的結(jié)果大致上是一樣的。
關(guān)鍵點(diǎn)
$只是一個(gè)方法,它是jQuery方法的簡寫也是它另一個(gè)名字。
點(diǎn)
在$(document)之后的點(diǎn)(’.')預(yù)示著有許多方法可以被調(diào)用。一定要是一個(gè)JS對象才能使用這個(gè)點(diǎn)。說個(gè)最簡單的,一個(gè)JS對象就是一堆屬性的集合:
var digger = new Object(); digger.species = "gerbil"; digger.name = "Digger"; digger.color = "white";
上面代碼中,變量digger是一個(gè)對象,我們賦值了三個(gè)子對象給它:species,name和color。在面向?qū)ο缶幊讨校@三個(gè)變量被稱為成員變量。你可能更簡潔地寫成這樣:
var digger = {species:"gerbil", name:"Digger", color:"white"};
你也可以把方法當(dāng)做屬性賦值給一個(gè)變量。沙鼠(Gerbil)大部分時(shí)候都很安靜,但他們偶爾也會(huì)發(fā)出高頻meeping sort of noise。在JS中,可以這么來表示:
function meepMeep(){
alert("meep meep");
}
在JS中,變量、方法和對象之間的界限是很模糊的,所以一個(gè)方法也可以被賦值到一個(gè)(成員)變量上:
digger.speak = meepMeep;
現(xiàn)在你可一個(gè)調(diào)用這個(gè)方法來讓沙鼠發(fā)出叫聲來:
digger.speak();
在面向?qū)ο笳Z法中,digger.speak();是一個(gè)成員方法或者函數(shù)。在同一個(gè)對象中的方法可以互相引用,它們還能引用其他的成員變量。想象一下Digger學(xué)會(huì)了說英語,這得是多牛X的一件事啊:
function myNameIs(){
alert("Meep! I am a " + this.species);
}
//assign the function
digger.sayMyName = myNameIs;
//call the function
digger.sayMyName();
在myNameIs函數(shù)中,this指的是包含它的對象,this.species就是digger.species,它的值就是’gerbil’。如果你想要不通過對象直接調(diào)用myNameIs(),那么this指的就是JS的window對象,this.species就是window.species,,在這里它的值是undefined。頁面的彈框中的文字會(huì)變成”Meep! I am a undefined”。
對象也可以作為函數(shù)的返回值,我個(gè)人也推薦這么使用:
function giveMeTheGerbil(){
return digger;
}
這么寫的話,返回的就是(全局)變量/對象digger的引用,它和最初的digger是完全一樣的,所以操作它的時(shí)候你不需要有什么顧慮。
var digger2 = giveMeTheGerbil(); //alerts "Meep! I am a gerbil" digger2.sayMyName();
你也可以不通過digger2這個(gè)中間值,而是直接在giveMeTheGerbil的返回值上調(diào)用sayMayName:
giveMeTheGerbil().sayMyName();
先不考慮內(nèi)部代碼,這種程序結(jié)構(gòu)和例子里第一行代碼是一樣的:
$(document).ready(…);
下一節(jié)你將知道ready的作用是什么。
關(guān)鍵點(diǎn)
將對象簡寫:{name:”Digger”, species:”gerbil”},在方法中使用到的this是依附于一個(gè)對象(或者一個(gè)方法)的,它指向包含它的對象。
匿名函數(shù)
在JS中,創(chuàng)建函數(shù)的方法多種多樣。只要你有一點(diǎn)編程經(jīng)驗(yàn)?zāi)菍ο旅娴暮瘮?shù)聲明就不會(huì)陌生:
function meepMeep(){
alert("meep meep");
}
在上文里我們已經(jīng)知道了函數(shù)是可以被賦值到變量上的。我們創(chuàng)建了meepMeep函數(shù),并將它賦值到digger.speak上。事實(shí)上,函數(shù)還可以被匿名地創(chuàng)建出來(我們稱呼這樣的函數(shù)為:函數(shù)表達(dá)式),它們在聲明時(shí)是沒有任何名字的,聲明后再被賦值到一個(gè)變量上:
var meepMeep = function(){
alert("meep meep");
};
在JS中,函數(shù)可以被賦值到變量上,還能像變量一樣到處傳遞。讓我們看看下面這個(gè)例子:
function runMe(f){
f();
}
runMe函數(shù)有一個(gè)傳入?yún)?shù)f,它將這個(gè)傳入?yún)?shù)視作一個(gè)函數(shù),還調(diào)用的這個(gè)函數(shù)。所以你可以這么使用runMe:
runMe(function(){
alert("meep meep");
});
這樣meepMeep函數(shù)就會(huì)被成功調(diào)用。如果在這個(gè)方法里,你連meepMeep的名字都不需要了,那事情就會(huì)更有趣些了。你可以直接創(chuàng)建它,當(dāng)需要的時(shí)候再把它傳入runMe來調(diào)用這個(gè)函數(shù):
meepMeep();
事實(shí)上,哪里都會(huì)出現(xiàn)meepMeep,等同于它的匿名函數(shù)也是這樣的。這么調(diào)用:
(function(){
alert("meep meep");
})();
不像上面那樣,你可以用匿名函數(shù)替換掉meepMeep,雖然使用匿名函數(shù)的時(shí)候你需要在最外層添加一組括號:
在JS中,這種寫法常常是用在制造變量作用域上。你能不能猜到下面這段代碼的輸出是什么呢?
var x=3;
(function(){
var x=4; console.log("x is " + x);
})();
console.log ("x is " + x);
在匿名函數(shù)里的var是解題的關(guān)鍵點(diǎn)。通過var,我們在函數(shù)內(nèi)定義了一個(gè)局部變量x,它的值是4,然后通過console.log輸出這個(gè)值。因?yàn)関ar這個(gè)關(guān)鍵詞,函數(shù)內(nèi)的x和函數(shù)外的值為3的x就是互相獨(dú)立的。因此這段代碼會(huì)將4和3先后打印出來。
現(xiàn)在我們的沙鼠已經(jīng)不會(huì)發(fā)出尖銳的聲音了,所以在代碼中我們不再使用alert改用console.log來打印它的結(jié)果。在現(xiàn)代瀏覽器中console.log*是可以使用的(換言之,IE瀏覽器低版本中無法使用它),使用console.log就能安靜地在瀏覽器控制臺中輸出信息。
我們接著就要講匿名函數(shù)了。jQuery的ready方法可以說是上文中的runMe函數(shù)的延時(shí)版。ready方法中的內(nèi)容會(huì)等到DOM完全加載完會(huì)后在運(yùn)行。所以等到document加載完成了,下面的匿名函數(shù)才會(huì)運(yùn)行:
function(){
$("button").click (…)
}
如果需要在HTML文檔加載完后再執(zhí)行一些動(dòng)作的話,程序員們通常會(huì)使用$(document).ready(…)。
關(guān)鍵點(diǎn)
匿名函數(shù)就是沒有名字的函數(shù),像function(){alert(1);}這樣。它們可以被賦值到變量上、被傳遞到其他函數(shù)中也可以立即執(zhí)行以創(chuàng)建出一個(gè)作用域來。
方法鏈
在更詳細(xì)地分析代碼之前,我們要先介紹JS中一個(gè)常見的內(nèi)容:方法鏈。方法鏈指的是在一行代碼中執(zhí)行多個(gè)函數(shù)。這真的只是上述giveMeTheGerbil()的一個(gè)擴(kuò)展:
giveMeTheGerbil().sayMyName();
現(xiàn)在讓我們要重新定義一下gerbil相關(guān)的方法來返回他們的引用。
digger.speak = function(){
alert("meep meep"); return this;
}
digger.sayMyName = function(){
alert("Meep! I am a " + this.species); return this;
}
這兩個(gè)函數(shù)都是對digger做了一些處理后返回digger對象。代碼沒有做什么改動(dòng),但是將digger對象返回以后,就可以把函數(shù)串在一起使用:
giveMeTheGerbil().speak().sayMyName().speak();
giveMeTheGerbil先運(yùn)行,返回了digger對象的引用。所以上面那行代碼等價(jià)于:
digger.speak().sayMyName().speak();
下一步,digger對象的speak方法運(yùn)行后彈窗出’meep meep’。這也能返回digger的引用,然后這行代碼就變成:
digger.sayMyName().speak();
在這之后,sayMyName運(yùn)行后返回digger的引用……運(yùn)行后會(huì)出現(xiàn)三個(gè)警告框:‘meep meep. Meep! I am a gerbil, meep meep’。這樣的鏈?zhǔn)叫Ч33霈F(xiàn)在JS中,你可能在字符串(string)對象中見到這個(gè):
var s = "I have a dagger.";
console.log(s.substring(9, 15).replace("a", "i").toUpperCase());
上面的代碼是獲取字符串s中的子字符串,再將子字符串中的字母’a'用’i'代替,替換后的結(jié)果(也就是’digger’)被轉(zhuǎn)為大寫,然后返回打印到控制臺上。
當(dāng)然,jQuery中到處都是方法鏈,在我們的例子中也能看到:
$("div").animate({height:"toggle"}).append("hi");
$(“div”)將頁面上所有的div元素獲取到然后作為jQuery對象的一部分返回。基于jQuery對象調(diào)用animate方法,然后再在每個(gè)jQuery對象上執(zhí)行append。這樣的作用鏈可以很長很長,下面這個(gè)是典型的長jQuery方法鏈:
總的來說,使用這樣的長方法鏈會(huì)造成debug和維護(hù)代碼的困難。所以盡量避免使用這樣的長鏈,不過在壓縮時(shí)它們還是常常被使用。
關(guān)鍵點(diǎn)
對象(比如對象中的方法)的方法會(huì)返回對象的引用,然后就能基于引用使用方法鏈,而不需要在執(zhí)行多個(gè)方法的時(shí)候還要儲(chǔ)存中間值。
jQuery對象
我們的例子里用了好幾個(gè)jQuery方法:ready、click、animate和append。這些方法都是與jQuery對象結(jié)合使用的,和上文中digger對象的speak方法和myNameIs方法類似的機(jī)制,也和string對象的substr方法、replace方法和toUpperCase方法類似。
這些函數(shù)都是jQuery對象的方法,它們也都會(huì)返回一個(gè)jQuery對象。不過比起我們例子里的digger對象和string對象,jQuery對象相對而言要復(fù)雜許多。就像早前提過的,JS中各個(gè)概念之前的界限其實(shí)比較模糊。你可以在使用方法鏈的時(shí)候把它視作一個(gè)對象,但是你也可以把它當(dāng)做一個(gè)數(shù)組來對待:
var mydivs = $("div");
for (var i = 0; i < mydivs.length; i++) {console.log(mydivs[i].innerHTML);}
在這里例子中,$(“div”)將頁面上所有的div元素都存儲(chǔ)一個(gè)jQuery對象中,然后賦值到變量mydivs中。這個(gè)jQuery對象會(huì)被當(dāng)做一個(gè)數(shù)組(其實(shí)是一個(gè)NodeList)進(jìn)入迭代。每次迭代都會(huì)對DOM中選出的節(jié)點(diǎn)做一些操作,這些節(jié)點(diǎn)在迭代里也是當(dāng)做對象的,所以它們也有自己的屬性,比如outerHTML和innerHTML。
也可以先把這些節(jié)點(diǎn)轉(zhuǎn)成jQuery對象,也就是在取得節(jié)點(diǎn)后將它們用$()包起來(你可以把任何代碼傳入$中,都能將它們轉(zhuǎn)成jQuery對象),再之后通過jQuery方法html()也可以得到相同的結(jié)果。
var mydivs = $("div");
for (var i = 0; i < mydivs.length; i++) {console.log($(mydivs[i]).html());}
上面兩個(gè)方法都可以將頁面上的div元素中的HTML內(nèi)容打印到控制臺中。
當(dāng)你在運(yùn)行像$(“div”).animate(…).append(…);這樣的代碼的時(shí)候,動(dòng)畫是會(huì)發(fā)生在所有的div元素上的,然后這些div元素會(huì)被作為jQuery對象的一部分傳到方法鏈中的下一個(gè)函數(shù)中(在大部分jQuery函數(shù)中都是這么實(shí)現(xiàn)的,具體請看文檔)。
關(guān)鍵點(diǎn)
jQuery的$函數(shù)還有像click、animate這樣會(huì)返回jQuery對象的方法,它們都是對象或者數(shù)組的一部分。類似數(shù)組的這部分會(huì)包含DOM中節(jié)點(diǎn)的引用。
總的來看
現(xiàn)在我們可以全局地來看這個(gè)例子了,$(document)返回的是頁面本身的jQuery對象。將一個(gè)方法傳入.ready(…)中,等到頁面已經(jīng)解析完了DOM也已經(jīng)加載完成,ready(…)中的方法就會(huì)運(yùn)行。
function(){
$("button").click(…);
}
這個(gè)方法將頁面中的button元素都獲取到了,然后返回一個(gè)綁定了click方法的jQuery對象。click方法中還有一個(gè)匿名函數(shù):
function(){
$("div").animate ({height:"toggle"}).append("hi");
}
上述的函數(shù)獲取了所有的div元素,然后返回一個(gè)jQuery對象,在這個(gè)對象上顯示調(diào)用了它的animate方法。傳入jQuery的animate方法中的參數(shù)是animate的一系列屬性,這些屬性是對象的簡寫形式,{height:”toggle”}這句是告訴jQuery對頁面上所有的div元素的高度都使用toggle效果:一開始div的高度會(huì)變成0,接著它們的高度又會(huì)動(dòng)畫地變回原來的值。
animate方法也會(huì)返回一個(gè)jQuery對象,執(zhí)行完animate方法后執(zhí)行append方法:每當(dāng)button被點(diǎn)擊了,就在每個(gè)div元素中添加”hi”字符串。運(yùn)行下面的HTML代碼來看看我們說的效果是什么樣的,在線demo在此:
<button>Click me</button>
<div style="width:100px;height:100px;background:green;"></div>
<script src="http://code.jquery.com/jquery-1.8.3.js"></script>
<script>
$(document).ready(function(){
$("button").click(function(){
$("div").animate({height:"toggle"}).append("hi");
});
});
</script>
每次button被點(diǎn)擊了,綠色的div就會(huì)收起或者展開,然后添加一個(gè)新的“hi”到div中。
事件驅(qū)動(dòng)造成的問題
下面這段代碼看起來夠簡單的吧:
//set h to 200
var h = 200;
$(document).ready(function(){
$("button").click(function(){
//animate up to h, 200 pixels high
$("div").animate({height:h});
});
});
你可能只是希望div的高度到200px,但是事實(shí)上從*h*被賦值為200到動(dòng)畫真正發(fā)生之間還可能發(fā)生了很多事情導(dǎo)致最終的結(jié)果和你所期望的不一樣。在一個(gè)復(fù)雜的jQuery應(yīng)用中,變量*h*可能會(huì)被反復(fù)使用或者它的值被改寫。你可能會(huì)發(fā)現(xiàn)div的高度只會(huì)達(dá)到50px而不是期望中的200px。這時(shí)候你需要去看看是不是別的代碼改寫了h的值,當(dāng)執(zhí)行*for (h=1; h<50; h++) {…}*來改變h的值時(shí),你可能會(huì)有所發(fā)現(xiàn)。
坦白來說,這個(gè)問題并不是由jQuery或者匿名函數(shù)造成的,而是事件驅(qū)動(dòng)編程本身就會(huì)遇到的問題。上述的代碼的片段其實(shí)是在不同的時(shí)間點(diǎn)被執(zhí)行的:
- 首次執(zhí)行時(shí)($(document).ready(…))
- 頁面加載完成后($(“button”).click(…))
- button被點(diǎn)擊后($(“div”).animate(…))
服務(wù)端的代碼(比如PHP的程序)運(yùn)行是有按照從頭到尾的順序的, 從開始到結(jié)束,輸入HTML以顯示頁面。JS也可以做到這一點(diǎn),但是它如果和事件結(jié)合起來才能發(fā)揮最大作用,比如button點(diǎn)擊事件。這就是事件驅(qū)動(dòng)編程,可不僅僅只有JS是這樣的編程哦。手機(jī)應(yīng)用背后的程序很多也都是事件驅(qū)動(dòng)的,比如Objective-C、Java或者C++在處理用戶與屏幕互動(dòng)這塊也是使用事件驅(qū)動(dòng)編程的。
如果上面的代碼轉(zhuǎn)成Java后再Android手機(jī)中運(yùn)行,那么在最里層的函數(shù)中的h的引用就會(huì)出現(xiàn)錯(cuò)誤。這是因?yàn)?em>h并沒有被聲明為全局(或者是Java中的static)變量,所以里層的代碼不知道h的值應(yīng)該是什么。雖然了解這點(diǎn)也解決不了事件驅(qū)動(dòng)造成的問題,不過至少以后你會(huì)想清楚要怎么使用變量。
避免上述問題的一個(gè)解決辦法就是將你的變量放在適當(dāng)?shù)淖饔糜蛑?。在第一個(gè)匿名函數(shù)中聲明var h變量來解決這個(gè)問題,這樣局部變量h的優(yōu)先級高于其他任何的全局變量h:
$(document).ready (function(){
//set h to 200
var h = 200;
$("button").click (function(){
//animate up to h, 200 pixels high
$("div").animate ({height:h});
});
});
如果你一定要使用全局變量,那就將這些全局變量命名、組合好,并在你的代碼中加上適當(dāng)?shù)腸omment:
//properties of the animation
var animationConfig = {upToHeight:200};
//when document is loaded
$(document).ready(function(){
//when any <button> element is clicked
$("button").click(function(){
//change the height of all <div>s
$("div").animate({height:animationConfig.upToHeight});
});
});
結(jié)論
這篇文章是一篇針對初學(xué)者的介紹JS語法和如何使用jQuery使用的指南。jQuery只是一個(gè)JS庫,它有一個(gè)很看起來很特別的函數(shù):$,推薦在jQuery中使用對象的簡寫形式、匿名函數(shù)還有方法鏈。類似的庫還有YUI(Yahoo User Interface)。
現(xiàn)在再看jQuery的代碼時(shí),你是不是不會(huì)再抱有過去的疑問和不確定了呢?你已經(jīng)知道它要做什么了。雖然由于事件驅(qū)動(dòng)編程的復(fù)雜性,你可能不確定什么時(shí)候使用它,但是你會(huì)知道怎么做。
原文鏈接: smashingmagazine 翻譯: 伯樂在線 - kmokidd
哈爾濱品用軟件有限公司致力于為哈爾濱的中小企業(yè)制作大氣、美觀的優(yōu)秀網(wǎng)站,并且能夠搭建符合百度排名規(guī)范的網(wǎng)站基底,使您的網(wǎng)站無需額外費(fèi)用,即可穩(wěn)步提升排名至首頁。歡迎體驗(yàn)最佳的哈爾濱網(wǎng)站建設(shè)。
