你是否曾經見過像$(".cta").click(function(){})這樣的JavaScrip代碼?或許你還會思考下$(‘#X‘)是什麼,若是看到這些你都以爲摸不着頭腦,那請必定要讀完這篇文章。若是你以爲上述的代碼片斷是不能正常工做的,那請先看一些jQuery的代碼範例,你會發現連接中的代碼都是這樣的結構。html
這篇文章將會分析下面代碼片斷(動畫化一個方形)中出現的一些關鍵知識點。你可能不會常常接觸這樣的代碼,但瞭解一下這段代碼的機制有助於你理解jQuery:
- $(document).ready(function(){
- $("button").click(function(){
- $("div").animate({height:"toggle"}).append("hi");
- });
- });
咱們將會逐字逐句地解釋上述的代碼,告訴你JavaScript函數、jQuery對象還有事件驅動編程的具體細節。但願看完這篇文章之後,再遇到神祕的jQuery代碼時你不會再頭疼。
$是什麼?
在你第一眼看到$的時候,有一種高大上的猜想在你心中盤旋:這必定是個很特別很複雜的JS方法。事實上,它很普通,也沒有什麼特殊的含義。$就是一個函數,是jQuery函數的另外一個名字罷了。
jQuery是一個比較簡單的JavaScript庫,它在瀏覽器兼容方面作得很好,並且還提供了許多頗有用的特性用來操做網頁或者作些動畫效果。
你能夠先引入jQuery庫的地址,而後就能使用jQuery函數(好比$)了:
- <script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
或者你也能夠直接在jQuery官網上下載它。
jQuery函數一般只須要一個參數,這個參數能夠是一個選擇器也能夠是JS引用頁面上的內容(好比document)。
選擇器就是CSS的一個片斷,寫在{…}以前的內容就是選擇器了。因此,$(「div」)和jQuery(「div」)是一個意思,就是簡單粗暴地把頁面上全部的<div>標籤都選中,和在CSS中使用.div得到的是同一個結果。
還記得在代碼的最開頭有一個$(document)嗎?這一步是要把JS變量document傳入jQuery方法當中。document是由瀏覽器來定義的,能夠直接使用,它指的是文檔對象模型(DOM)的最頂層。DOM指的是各個瀏覽器是如何來解釋頁面的整個HTML結構。用jQuery寫的程序是基於DOM的。
jQuery中的$(‘div’)和document.getElementsByTagNae(「div」)獲得的結果大體上是同樣的。
關鍵點
$只是一個方法,它是jQuery方法的簡寫也是它另外一個名字。
點
在$(document)以後的點(’.‘)預示着有許多方法能夠被調用。必定要是一個JS對象才能使用這個點。說個最簡單的,一個JS對象就是一堆屬性的集合:
- var digger = new Object();
- digger.species = "gerbil";
- digger.name = "Digger";
- digger.color = "white";
上面代碼中,變量digger是一個對象,咱們賦值了三個子對象給它:species,name和color。在面向對象編程中,這三個變量被稱爲成員變量。你可能更簡潔地寫成這樣:
- var digger = {species:"gerbil", name:"Digger", color:"white"};
你也能夠把方法當作屬性賦值給一個變量。沙鼠(Gerbil)大部分時候都很安靜,但他們偶爾也會發出高頻meeping sort of noise。在JS中,能夠這麼來表示:
- function meepMeep(){
- alert("meep meep");
- }
在JS中,變量、方法和對象之間的界限是很模糊的,因此一個方法也能夠被賦值到一個(成員)變量上:
在面向對象語法中,digger.speak();是一個成員方法或者函數。在同一個對象中的方法能夠互相引用,它們還能引用其餘的成員變量。想象一下Digger學會了說英語,這得是多牛X的一件事啊:
- function myNameIs(){
- alert("Meep! I am a " + this.species);
- }
- //assign the function
- digger.sayMyName = myNameIs;
- //call the function
- digger.sayMyName();
在myNameIs函數中,this指的是包含它的對象,this.species就是digger.species,它的值就是’gerbil’。若是你想要不經過對象直接調用myNameIs(),那麼this指的就是JS的window對象,this.species就是window.species,,在這裏它的值是undefined。頁面的彈框中的文字會變成」Meep! I am a undefined」。
對象也能夠做爲函數的返回值,我我的也推薦這麼使用:
- function giveMeTheGerbil(){
- return digger;
- }
這麼寫的話,返回的就是(全局)變量/對象digger的引用,它和最初的digger是徹底同樣的,因此操做它的時候你不須要有什麼顧慮。
- var digger2 = giveMeTheGerbil();
- //alerts "Meep! I am a gerbil"
- digger2.sayMyName();
你也能夠不經過digger2這個中間值,而是直接在giveMeTheGerbil的返回值上調用sayMayName:
- giveMeTheGerbil().sayMyName();
先不考慮內部代碼,這種程序結構和例子裏第一行代碼是同樣的:
關鍵點
將對象簡寫:{name:」Digger」, species:」gerbil」},在方法中使用到的this是依附於一個對象(或者一個方法)的,它指向包含它的對象。
匿名函數
在JS中,建立函數的方法多種多樣。只要你有一點編程經驗那對下面的函數聲明就不會陌生:
- function meepMeep(){
- alert("meep meep");
- }
在上文裏咱們已經知道了函數是能夠被賦值到變量上的。咱們建立了meepMeep函數,並將它賦值到digger.speak上。事實上,函數還能夠被匿名地建立出來(咱們稱呼這樣的函數爲:函數表達式),它們在聲明時是沒有任何名字的,聲明後再被賦值到一個變量上:
- var meepMeep = function(){
- alert("meep meep");
- };
在JS中,函數能夠被賦值到變量上,還能像變量同樣處處傳遞。讓咱們看看下面這個例子:
- function runMe(f){
- f();
- }
runMe函數有一個傳入參數f,它將這個傳入參數視做一個函數,還調用的這個函數。因此你能夠這麼使用runMe:
- runMe(function(){
- alert("meep meep");
- });
這樣meepMeep函數就會被成功調用。若是在這個方法裏,你連meepMeep的名字都不須要了,那事情就會更有趣些了。你能夠直接建立它,當須要的時候再把它傳入runMe來調用這個函數:
事實上,哪裏都會出現meepMeep,等同於它的匿名函數也是這樣的。這麼調用:
- (function(){
- alert("meep meep");
- })();
不像上面那樣,你能夠用匿名函數替換掉meepMeep,雖然使用匿名函數的時候你須要在最外層添加一組括號:
在JS中,這種寫法經常是用在製造變量做用域上。你能不能猜到下面這段代碼的輸出是什麼呢?
- var x=3;
- (function(){
- var x=4; console.log("x is " + x);
- })();
- console.log ("x is " + x);
在匿名函數裏的var是解題的關鍵點。經過var,咱們在函數內定義了一個局部變量x,它的值是4,而後經過console.log輸出這個值。由於var這個關鍵詞,函數內的x和函數外的值爲3的x就是互相獨立的。所以這段代碼會將4和3前後打印出來。
如今咱們的沙鼠已經不會發出尖銳的聲音了,因此在代碼中咱們再也不使用alert改用console.log來打印它的結果。在現代瀏覽器中console.log*是可使用的(換言之,IE瀏覽器低版本中沒法使用它),使用console.log就能安靜地在瀏覽器控制檯中輸出信息。
咱們接着就要講匿名函數了。jQuery的ready方法能夠說是上文中的runMe函數的延時版。ready方法中的內容會等到DOM徹底加載完會後在運行。因此等到document加載完成了,下面的匿名函數纔會運行:
- function(){
- $("button").click (…)
- }
若是須要在HTML文檔加載完後再執行一些動做的話,程序員們一般會使用
關鍵點
匿名函數就是沒有名字的函數,像function(){alert(1);}這樣。它們能夠被賦值到變量上、被傳遞到其餘函數中也能夠當即執行以建立出一個做用域來。
方法鏈
在更詳細地分析代碼以前,咱們要先介紹JS中一個常見的內容:方法鏈。方法鏈指的是在一行代碼中執行多個函數。這真的只是上述giveMeTheGerbil()的一個擴展:
- giveMeTheGerbil().sayMyName();
如今讓咱們要從新定義一下gerbil相關的方法來返回他們的引用。
- digger.speak = function(){
- alert("meep meep"); return this;
- }
- digger.sayMyName = function(){
- alert("Meep! I am a " + this.species); return this;
- }
這兩個函數都是對digger作了一些處理後返回digger對象。代碼沒有作什麼改動,可是將digger對象返回之後,就能夠把函數串在一塊兒使用:
- giveMeTheGerbil().speak().sayMyName().speak();
giveMeTheGerbil先運行,返回了digger對象的引用。因此上面那行代碼等價於:
- digger.speak().sayMyName().speak();
下一步,digger對象的speak方法運行後彈窗出’meep meep’。這也能返回digger的引用,而後這行代碼就變成:
- digger.sayMyName().speak();
在這以後,sayMyName運行後返回digger的引用……運行後會出現三個警告框:‘meep meep. Meep! I am a gerbil, meep meep’。這樣的鏈式效果經常出如今JS中,你可能在字符串(string)對象中見到這個:
- var s = "I have a dagger.";
- console.log(s.substring(9, 15).replace("a", "i").toUpperCase());
上面的代碼是獲取字符串s中的子字符串,再將子字符串中的字母’a‘用’i‘代替,替換後的結果(也就是’digger’)被轉爲大寫,而後返回打印到控制檯上。
固然,jQuery中處處都是方法鏈,在咱們的例子中也能看到:
- $("div").animate({height:"toggle"}).append("hi");
$(「div」)將頁面上全部的div元素獲取到而後做爲jQuery對象的一部分返回。基於jQuery對象調用animate方法,而後再在每一個jQuery對象上執行append。這樣的做用鏈能夠很長很長,下面這個是典型的長jQuery方法鏈:
總的來講,使用這樣的長方法鏈會形成debug和維護代碼的困難。因此儘可能避免使用這樣的長鏈,不過在壓縮時它們仍是經常被使用。
關鍵點
對象(好比對象中的方法)的方法會返回對象的引用,而後就能基於引用使用方法鏈,而不須要在執行多個方法的時候還要儲存中間值。
jQuery對象
咱們的例子裏用了好幾個jQuery方法:ready、click、animate和append。這些方法都是與jQuery對象結合使用的,和上文中digger對象的speak方法和myNameIs方法相似的機制,也和string對象的substr方法、replace方法和toUpperCase方法相似。
這些函數都是jQuery對象的方法,它們也都會返回一個jQuery對象。不過比起咱們例子裏的digger對象和string對象,jQuery對象相對而言要複雜許多。就像早前提過的,JS中各個概念以前的界限其實比較模糊。你能夠在使用方法鏈的時候把它視做一個對象,可是你也能夠把它當作一個數組來對待:
- var mydivs = $("div");
- for (var i = 0; i < mydivs.length; i++) {console.log(mydivs[i].innerHTML);}
在這裏例子中,$(「div」)將頁面上全部的div元素都存儲一個jQuery對象中,而後賦值到變量mydivs中。這個jQuery對象會被當作一個數組(實際上是一個NodeList)進入迭代。每次迭代都會對DOM中選出的節點作一些操做,這些節點在迭代裏也是當作對象的,因此它們也有本身的屬性,好比outerHTML和innerHTML。
也能夠先把這些節點轉成jQuery對象,也就是在取得節點後將它們用$()包起來(你能夠把任何代碼傳入$中,都能將它們轉成jQuery對象),再以後經過jQuery方法html()也能夠獲得相同的結果。
- var mydivs = $("div");
- for (var i = 0; i < mydivs.length; i++) {console.log($(mydivs[i]).html());}
上面兩個方法均可以將頁面上的div元素中的HTML內容打印到控制檯中。
當你在運行像$(「div」).animate(…).append(…);這樣的代碼的時候,動畫是會發生在全部的div元素上的,而後這些div元素會被做爲jQuery對象的一部分傳到方法鏈中的下一個函數中(在大部分jQuery函數中都是這麼實現的,具體請看文檔)。
關鍵點
jQuery的$函數還有像click、animate這樣會返回jQuery對象的方法,它們都是對象或者數組的一部分。相似數組的這部分會包含DOM中節點的引用。
總的來看
如今咱們能夠全局地來看這個例子了,$(document)返回的是頁面自己的jQuery對象。將一個方法傳入.ready(…)中,等到頁面已經解析完了DOM也已經加載完成,ready(…)中的方法就會運行。
- function(){
- $("button").click(…);
- }
這個方法將頁面中的button元素都獲取到了,而後返回一個綁定了click方法的jQuery對象。click方法中還有一個匿名函數:
- function(){
- $("div").animate({height:"toggle"}).append("hi");
- }
上述的函數獲取了全部的div元素,而後返回一個jQuery對象,在這個對象上顯示調用了它的animate方法。傳入jQuery的animate方法中的參數是animate的一系列屬性,這些屬性是對象的簡寫形式,{height:」toggle」}這句是告訴jQuery對頁面上全部的div元素的高度都使用toggle效果:一開始div的高度會變成0,接着它們的高度又會動畫地變回原來的值。
animate方法也會返回一個jQuery對象,執行完animate方法後執行append方法:每當button被點擊了,就在每一個div元素中添加」hi」字符串。運行下面的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被點擊了,綠色的div就會收起或者展開,而後添加一個新的「hi」到div中。
事件驅動形成的問題
下面這段代碼看起來夠簡單的吧:
- //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,可是事實上從*h*被賦值爲200到動畫真正發生之間還可能發生了不少事情致使最終的結果和你所指望的不同。在一個複雜的jQuery應用中,變量*h*可能會被反覆使用或者它的值被改寫。你可能會發現div的高度只會達到50px而不是指望中的200px。這時候你須要去看看是否是別的代碼改寫了h的值,當執行*for (h=1; h<50; h++) {…}*來改變h的值時,你可能會有所發現。
坦白來講,這個問題並非由jQuery或者匿名函數形成的,而是事件驅動編程自己就會遇到的問題。上述的代碼的片斷實際上是在不一樣的時間點被執行的:
- 首次執行時($(document).ready(…))
- 頁面加載完成後($(「button」).click(…))
- button被點擊後($(「div」).animate(…))
服務端的代碼(好比PHP的程序)運行是有按照從頭至尾的順序的, 從開始到結束,輸入HTML以顯示頁面。JS也能夠作到這一點,可是它若是和事件結合起來才能發揮最大做用,好比button點擊事件。這就是事件驅動編程,可不只僅只有JS是這樣的編程哦。手機應用背後的程序不少也都是事件驅動的,好比Objective-C、Java或者C++在處理用戶與屏幕互動這塊也是使用事件驅動編程的。
若是上面的代碼轉成Java後再Android手機中運行,那麼在最裏層的函數中的h的引用就會出現錯誤。這是由於h並無被聲明爲全局(或者是Java中的static)變量,因此裏層的代碼不知道h的值應該是什麼。雖然瞭解這點也解決不了事件驅動形成的問題,不過至少之後你會想清楚要怎麼使用變量。
避免上述問題的一個解決辦法就是將你的變量放在適當的做用域中。在第一個匿名函數中聲明var h變量來解決這個問題,這樣局部變量h的優先級高於其餘任何的全局變量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});
- });
- });
若是你必定要使用全局變量,那就將這些全局變量命名、組合好,並在你的代碼中加上適當的comment:
- //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});
- });
- });
結論
這篇文章是一篇針對初學者的介紹JS語法和如何使用jQuery使用的指南。jQuery只是一個JS庫,它有一個很看起來很特別的函數:$,推薦在jQuery中使用對象的簡寫形式、匿名函數還有方法鏈。相似的庫還有YUI(Yahoo User Interface)。
如今再看jQuery的代碼時,你是否是不會再抱有過去的疑問和不肯定了呢?你已經知道它要作什麼了。雖然因爲事件驅動編程的複雜性,你可能不肯定何時使用它,可是你會知道怎麼作。