你是否曾經見過像  $(".cta").click(function(){})這樣的JavaScrip代碼?或許你還會思考下 $('#X') 是什么,如果看到這些你都覺得摸不著頭腦,那請一定要讀完這篇文章。如果你覺得上述的代碼片段是不能正常工作的,那請先看一些jQuery的代碼范例,你會發(fā)現(xiàn)鏈接中的代碼都是這樣的結構。

  這篇文章將會分析下面代碼片段(動畫化一個方形)中出現(xiàn)的一些關鍵知識點。你可能不會經常接觸這樣的代碼,但了解一下這段代碼的機制有助于你理解jQuery:

 $(document).ready(function(){
     $("button").click(function(){
         $("div").animate({height:"toggle"}).append("hi");
     });
 });

  我們將會逐字逐句地解釋上述的代碼,告訴你JavaScript函數、jQuery對象還有事件驅動編程的具體細節(jié)。希望看完這篇文章以后,再遇到神秘的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獲得的是同一個結果。

 <style>
     div {…}
 </style>

  還記得在代碼的最開頭有一個$(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)大部分時候都很安靜,但他們偶爾也會發(fā)出高頻meeping sort of noise。在JS中,可以這么來表示:

 function meepMeep(){
     alert("meep meep");
 }

  在JS中,變量、方法和對象之間的界限是很模糊的,所以一個方法也可以被賦值到一個(成員)變量上:

digger.speak = meepMeep;

  現(xiàn)在你可一個調用這個方法來讓沙鼠發(fā)出叫聲來:

digger.speak();

  在面向對象語法中,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();

  先不考慮內部代碼,這種程序結構和例子里第一行代碼是一樣的:

$(document).ready(…);

  下一節(jié)你將知道ready的作用是什么。

  關鍵點

  將對象簡寫:{name:”Digger”, species:”gerbil”},在方法中使用到的this是依附于一個對象(或者一個方法)的,它指向包含它的對象。

  匿名函數

  在JS中,創(chuàng)建函數的方法多種多樣。只要你有一點編程經驗那對下面的函數聲明就不會陌生:

 function meepMeep(){
     alert("meep meep");
 }

  在上文里我們已經知道了函數是可以被賦值到變量上的。我們創(chuàng)建了meepMeep函數,并將它賦值到digger.speak上。事實上,函數還可以被匿名地創(chuàng)建出來(我們稱呼這樣的函數為:函數表達式),它們在聲明時是沒有任何名字的,聲明后再被賦值到一個變量上:

 var meepMeep = function(){
     alert("meep meep");
 };

  在JS中,函數可以被賦值到變量上,還能像變量一樣到處傳遞。讓我們看看下面這個例子:

 function runMe(f){
     f();
 }

  runMe函數有一個傳入參數f,它將這個傳入參數視作一個函數,還調用的這個函數。所以你可以這么使用runMe:

 runMe(function(){
     alert("meep meep");
 });

  這樣meepMeep函數就會被成功調用。如果在這個方法里,你連meepMeep的名字都不需要了,那事情就會更有趣些了。你可以直接創(chuàng)建它,當需要的時候再把它傳入runMe來調用這個函數:

meepMeep();

  事實上,哪里都會出現(xiàn)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先后打印出來。

  現(xiàn)在我們的沙鼠已經不會發(fā)出尖銳的聲音了,所以在代碼中我們不再使用alert改用console.log來打印它的結果。在現(xiàn)代瀏覽器中console.log*是可以使用的(換言之,IE瀏覽器低版本中無法使用它),使用console.log就能安靜地在瀏覽器控制臺中輸出信息。

  我們接著就要講匿名函數了。jQuery的ready方法可以說是上文中的runMe函數的延時版。ready方法中的內容會等到DOM完全加載完會后在運行。所以等到document加載完成了,下面的匿名函數才會運行:

 function(){
     $("button").click (…)
 }

  如果需要在HTML文檔加載完后再執(zhí)行一些動作的話,程序員們通常會使用$(document).ready(…)。

  關鍵點

  匿名函數就是沒有名字的函數,像function(){alert(1);}這樣。它們可以被賦值到變量上、被傳遞到其他函數中也可以立即執(zhí)行以創(chuàng)建出一個作用域來。

  方法鏈

  在更詳細地分析代碼之前,我們要先介紹JS中一個常見的內容:方法鏈。方法鏈指的是在一行代碼中執(zhí)行多個函數。這真的只是上述giveMeTheGerbil()的一個擴展:

giveMeTheGerbil().sayMyName();

  現(xiàn)在讓我們要重新定義一下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的引用……運行后會出現(xiàn)三個警告框:‘meep meep. Meep! I am a gerbil, meep meep’。這樣的鏈式效果常常出現(xiàn)在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對象的一部分返回?;趈Query對象調用animate方法,然后再在每個jQuery對象上執(zhí)行append。這樣的作用鏈可以很長很長,下面這個是典型的長jQuery方法鏈

  總的來說,使用這樣的長方法鏈會造成debug和維護代碼的困難。所以盡量避免使用這樣的長鏈,不過在壓縮時它們還是常常被使用。

  關鍵點

  對象(比如對象中的方法)的方法會返回對象的引用,然后就能基于引用使用方法鏈,而不需要在執(zhí)行多個方法的時候還要儲存中間值。

  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中選出的節(jié)點做一些操作,這些節(jié)點在迭代里也是當做對象的,所以它們也有自己的屬性,比如outerHTML和innerHTML。

  也可以先把這些節(jié)點轉成jQuery對象,也就是在取得節(jié)點后將它們用$()包起來(你可以把任何代碼傳入$中,都能將它們轉成jQuery對象),再之后通過jQuery方法html()也可以得到相同的結果。

 var mydivs = $("div");
 for (var i = 0; i < mydivs.length; i++) {console.log($(mydivs[i]).html());}

  上面兩個方法都可以將頁面上的div元素中的HTML內容打印到控制臺中。

  當你在運行像$(“div”).animate(…).append(…);這樣的代碼的時候,動畫是會發(fā)生在所有的div元素上的,然后這些div元素會被作為jQuery對象的一部分傳到方法鏈中的下一個函數中(在大部分jQuery函數中都是這么實現(xiàn)的,具體請看文檔)。

  關鍵點

  jQuery的$函數還有像click、animate這樣會返回jQuery對象的方法,它們都是對象或者數組的一部分。類似數組的這部分會包含DOM中節(jié)點的引用。

  總的來看

  現(xiàn)在我們可以全局地來看這個例子了,$(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對象,執(zhí)行完animate方法后執(zhí)行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到動畫真正發(fā)生之間還可能發(fā)生了很多事情導致最終的結果和你所期望的不一樣。在一個復雜的jQuery應用中,變量*h*可能會被反復使用或者它的值被改寫。你可能會發(fā)現(xiàn)div的高度只會達到50px而不是期望中的200px。這時候你需要去看看是不是別的代碼改寫了h的值,當執(zhí)行*for (h=1; h<50; h++) {…}*來改變h的值時,你可能會有所發(fā)現(xiàn)。

  坦白來說,這個問題并不是由jQuery或者匿名函數造成的,而是事件驅動編程本身就會遇到的問題。上述的代碼的片段其實是在不同的時間點被執(zhí)行的:

  服務端的代碼(比如PHP的程序)運行是有按照從頭到尾的順序的, 從開始到結束,輸入HTML以顯示頁面。JS也可以做到這一點,但是它如果和事件結合起來才能發(fā)揮最大作用,比如button點擊事件。這就是事件驅動編程,可不僅僅只有JS是這樣的編程哦。手機應用背后的程序很多也都是事件驅動的,比如Objective-C、Java或者C++在處理用戶與屏幕互動這塊也是使用事件驅動編程的。

  如果上面的代碼轉成Java后再Android手機中運行,那么在最里層的函數中的h的引用就會出現(xiàn)錯誤。這是因為h并沒有被聲明為全局(或者是Java中的static)變量,所以里層的代碼不知道h的值應該是什么。雖然了解這點也解決不了事件驅動造成的問題,不過至少以后你會想清楚要怎么使用變量。

  避免上述問題的一個解決辦法就是將你的變量放在適當的作用域中。在第一個匿名函數中聲明var h變量來解決這個問題,這樣局部變量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});
     });
 });

  如果你一定要使用全局變量,那就將這些全局變量命名、組合好,并在你的代碼中加上適當的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)。

  現(xiàn)在再看jQuery的代碼時,你是不是不會再抱有過去的疑問和不確定了呢?你已經知道它要做什么了。雖然由于事件驅動編程的復雜性,你可能不確定什么時候使用它,但是你會知道怎么做。

  原文鏈接: smashingmagazine   翻譯: 伯樂在線 - kmokidd

  哈爾濱品用軟件有限公司致力于為哈爾濱的中小企業(yè)制作大氣、美觀的優(yōu)秀網站,并且能夠搭建符合百度排名規(guī)范的網站基底,使您的網站無需額外費用,即可穩(wěn)步提升排名至首頁。歡迎體驗最佳的哈爾濱網站建設。