jQuery物件入門級介紹

jobbole發表於2014-06-26

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

  這篇文章將會分析下面程式碼片段(動畫化一個方形)中出現的一些關鍵知識點。你可能不會經常接觸這樣的程式碼,但瞭解一下這段程式碼的機制有助於你理解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獲得的是同一個結果。

 <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)大部分時候都很安靜,但他們偶爾也會發出高頻meeping sort of noise。在JS中,可以這麼來表示:

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

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

digger.speak = meepMeep;

  現在你可一個呼叫這個方法來讓沙鼠發出叫聲來:

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(…);

  下一節你將知道ready的作用是什麼。

  關鍵點

  將物件簡寫:{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();

  事實上,哪裡都會出現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文件載入完後再執行一些動作的話,程式設計師們通常會使用$(document).ready(…)。

  關鍵點

  匿名函式就是沒有名字的函式,像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的程式碼時,你是不是不會再抱有過去的疑問和不確定了呢?你已經知道它要做什麼了。雖然由於事件驅動程式設計的複雜性,你可能不確定什麼時候使用它,但是你會知道怎麼做。

  原文連結: smashingmagazine   翻譯: 伯樂線上 - kmokidd

相關文章