天天看點

《JavaScript和jQuery實戰手冊(原書第2版)》——3.4節函數:把有用的代碼轉換為可複用的指令

3.4 函數:把有用的代碼轉換為可複用的指令

假設在工作中你剛剛得到一位新的助理,來幫助你完成每項任務(這時候應該把本書歸類為“玄幻小說”了)。假設你餓了想吃披薩餅,但助理是新來到這座大樓和這個區域的,你必須給他清楚的訓示:“出門右轉,乘電梯到1層,走出大樓……”等。助手按照你的訓示并且買來了一塊披薩餅。幾個小時之後,你又餓了,還想吃更多的披薩餅。現在,你不需要再次複述整個訓示(“出門右轉,乘電梯到1層,走出大樓……”)。這次,助手知道到哪裡去購買披薩餅,是以,你說“買塊披薩餅來”,他就會到有披薩餅的地方買來一塊。

換句話說,我們隻需要提供一次詳細訓示就夠了,助手記住這些步驟,并且隻要簡單地說“買塊披薩餅來”,他立即離開并且稍後帶着披薩餅回來。javascript也有與此等效的機制,叫做函數。函數是在腳本的開始處設定的一系列的程式設計步驟,等效于為助手提供的詳細訓示。當建立函數的時候,這些步驟并沒有實際運作;相反,它們存儲在web浏覽器的記憶體中,而在我們需要執行那些步驟的時候可以調用它們。

函數對于高效重複執行多個程式設計步驟很有價值:例如,假設要建立一個照片集web頁面,其中填滿了50幅小的縮略圖圖像。當某人單擊其中一幅小圖像時,我們可能希望頁面淡出,出現一個标題,并且該圖像的一個較大版本填充整個螢幕(将在7.4節學習如何做到這些)。每次某人單擊一幅圖像,就重複這個過程,是以,在具有50幅較小的照片的web頁面上,腳本可能必須做同樣的一系列步驟50次。幸運的是,我們不一定必須編寫同樣的代碼50次才能讓照片集工作。相反,可以編寫一個具有所有必需步驟的函數,随後,對于縮略圖的每次單擊,運作這個函數。編寫一次代碼,但是可以運作它任意多次。

函數的基本結構如下所示:

關鍵字function讓javascript解釋器知道我們要建立一個函數,這和使用if開始一條if/else語句或使用var來建立一個變量類似。接下來,我們提供了一個函數名,和變量一樣,我們也可以選擇自己的函數名。遵守2.4.1節列出的指令變量的規則。此外,在函數名中包含一個動詞是很常見的,例如calculatetax、getscreenheight、updatepage或fadeimage。帶有動詞的函數名使得人們很清楚這個函數做什麼,并且很容易區分函數名和變量名。

緊接着名字,我們添加了一對圓括号,這是函數的另一個特征。在圓括号的後面是一個空格,後面跟着一個花括号、一行或多行javascript代碼,最後是結束花括号。和if語句一樣,花括号表示組成函數的javascript代碼的開始和結束。

提示: 和if/else語句一樣,如果縮進了花括号之間的javascript代碼,函數會更容易閱讀。每行開始使用兩個空格(或一個制表符)是很常見的。

下面是一個簡單的函數,它以"sun may 12 2008"的格式顯示目前日期:

這個函數名為printtoday。它隻有兩行javascript代碼,接受目前日期,把日期轉換為我們可以了解的一種格式(即todatestring()部分),然後使用document.write()指令把結果顯示到頁面上。現在不必關心所有這些日期内容是如何工作的,我們将在14.4節中找到答案。

程式員通常把他們的函數放在腳本的開始處,在那裡設定了各種函數,以供腳本剩下的部分随後使用。别忘了,當初次建立函數的時候,它不會運作,這就好像告訴助手如何到達披薩店,而不是真的讓他去那裡。javascript代碼隻是存儲在浏覽器的記憶體裡,等到随後需要的時候才運作。

但是,我們如何運作一個函數呢?用程式設計的術語來說,在想要函數執行其任務的任何時候調用函數。調用函數隻是寫下函數名後面跟着一對圓括号就可以了。例如,要讓printtoday函數運作,直接輸入:

正如你所看到的,讓一個函數運作并不需要太多的錄入,這就是函數美麗的地方。一旦建立了函數,不需要添加太多的代碼就可以得到結果。

注意: 當調用一個函數的時候,不要忘了跟在函數後面的圓括号。這是讓函數運作起來的部分。例如,printtoday不做任何事情,但printtoday()執行該函數。

3.4.1 小教程

因為函數是如此重要的一個概念,是以這裡給出一系列的步驟供我們練習在真正的web頁面上建立和使用函數:

在文本編輯器中打開print_date.html。

首先在文檔的開始處添加一個函數。

找到頁面< head>中的< script>标記之間的代碼,并且錄入如下代碼:

基本的函數準備好了,但它現在還不能做任何事情。

儲存檔案并在web浏覽器中預覽。

什麼也沒發生。好了,實際上有些事情确實發生了,隻是我們沒有看到。web浏覽器把函數語句讀入記憶體,并且等待真正地調用函數,我們将在下面這麼做。

傳回文本編輯器和print_date.html檔案。找到以“today is”開始的

标簽,并在兩個< strong>标簽之間添加如下粗體所示的代碼:

儲存頁面并在web浏覽器中預覽。目前的日期顯示在頁面上。如果想要在web頁面的底部也顯示日期,所需要做的隻是再次調用函數。

3.4.2 給函數提供資訊

如果函數能接收到資訊就更有用了。回想一下你的助手,那個為你買披薩餅的人。3.4節所描述的最初的“函數”隻是說明了如何找到披薩店,訓示要購買一塊披薩餅然後傳回辦公室。當你想要某一塊披薩餅,就通過告訴助手“買一塊披薩餅”來“調用”這個函數。當然,根據你的口味,你可能想要意大利香腸披薩餅、奶酪披薩餅或者橄榄披薩餅。要讓你的指令更為靈活,可以告訴助手你喜歡什麼類型的披薩。每次你需要一塊披薩的時候,可以指定不同的類型。

javascript函數也可以接收資訊,這種資訊叫做參數,函數使用它們來執行操作。例如,如果想要建立一個函數來計算某人購物車的總金額,那麼,這個函數需要知道每件商品的價格,以及每件商品訂購的數量。

當建立這個函數的時候,在圓括号内放置一個新的變量的名字,這就是參數。基本結構如下所示:

這個函數的名字是print,并且它還擁有一個名為message的參數。當調用這個函數的時候,它接收一些資訊(要顯示的消息),然後它使用document.write()函數把消息顯示到頁面。當然,函數在調用之前不會做任何事情,是以,在web頁面的某個地方,我們可以像下面這樣調用函數:

這段代碼運作的時候,調用print函數并且把一些文本(字元串“hello world.”)發送到函數,然後在頁面上顯示出“hello world.”。從技術上講,向函數發送消息的這個過程叫做“傳遞一個參數”。在這個例子中,文本“hello world.”就是參數。

即便是像這樣一個真正簡單的函數,如果你是程式設計新手,事情何時發生以及如何發生的邏輯也有點兒容易令人混淆。下面是分解的每個步驟,如圖3-7所示:

《JavaScript和jQuery實戰手冊(原書第2版)》——3.4節函數:把有用的代碼轉換為可複用的指令

javascript解釋器讀入函數并存儲到記憶體中。這個步驟隻是準備web浏覽器稍後運作的函數。

調用該函數并且資訊“hello world.”傳遞給了函數。

傳遞給函數的資訊存儲在一個新的名為message的變量中。這個步驟等效于var message =‘hello world.’。

最後,函數運作,在web頁面上顯示變量message中存儲的值。

函數并不僅限于單個參數。我們可以給一個函數傳遞任意多個參數。隻需要在函數中指定每個參數,例如:

在這個例子中,我們給函數傳遞了兩個參數“hello world.”和“p”。這些值存儲在函數的兩個變量中,即message和tag中。結果是一個新的段落,

hello world.

顯示在頁面上。

不僅限于把字元串傳遞給函數,可以把任何類型的javascript變量或值傳遞給函數。例如,可以把數組、變量、值或一個boolean值作為一個參數傳遞。

3.4.3 從函數擷取資訊

有些時候,函數隻是做一些諸如此類的事情:如在頁面上寫消息,在螢幕上移動一個對象,或者驗證頁面上的表單字段。其他時候,我們還希望從函數取回一些資訊,畢竟,如果最終沒拿到某種可口的披薩餅,那就是“get me a slice of pizza”函數的效果并不是太好。同樣,計算購物車的商品總金額的函數,如果不能使我們知道最終的總金額,說明也不是很有用。

在一些内置的javascript函數中,我們已經看到了傳回值。例如,prompt()指令(參見2.7節)彈出了帶有一個文本字段的對話框,使用者在框中輸入的内容将被傳回。正如我們已經看到的,可以把傳回值存儲到一個變量中并且用它做些事情:

通路者對于提示對話框的響應存儲在變量answer中;然後,可以使用條件語句來測試變量中的值,或者對它做其他任何javascript允許對變量做的事情。

要從自己的函數傳回一個值,可以使用return關鍵字,其後跟着想要傳回的值:

第一行代碼把稅率存儲到一個名為tax的變量中(這使得我們隻需要更改這行代碼就很容易修改稅率)。接下來的3行代碼定義了函數。對于函數中發生的事情,現在不需要關心太多,我們将在14.3節學習如何操作數值。重要的部分是函數的第四行代碼——return語句。它傳回存儲在變量formattedtotal中的值。

為了利用傳回值,通常将其存儲在一個變量中,是以,在這個例子中,可以像下面這樣調用該函數:

在這個例子中,值2和16.95傳遞給函數。第一個數值表示購買的商品的數目,第二個數值表示其單價。結果從函數傳回,并且存儲在一個新變量saletotal中,該變量用作document.write()指令的一部分,用于顯示銷售的總金額(包含稅)。

注意: return關鍵字不應該是一個函數中的最後一句,因為隻要浏覽器的javascript解釋器遇到return語句,它會退出該函數。在函數中,跟在return語句之後的任何代碼行将不會執行。

然而,不一定必須把傳回值存儲在一個變量中。可以在另一條語句中直接使用傳回值,例如:

在這個例子中,調用函數并且将其傳回值添加到字元串“total: $”後面,然後顯示到文檔。首先,這種使用函數的方法很難閱讀,是以,我們可能希望采用一個額外的步驟隻是把函數的結果存儲到一個變量中,然後在腳本中使用該變量。

注意: 一個函數隻能傳回一個值。如果想要傳回多個項目,可以把值存儲在一個數組中并傳回數組。

3.4.4 防止變量沖突

函數的一個很好的優點是,它們可以減少必須編寫的程式的數量。我們可能會發現自己在不同的項目上一次又一次地使用一個真正有用的函數。例如,輔助計算運輸稅和銷售稅的函數可能對于我們所建立的每個訂單都有用,是以,可能會把函數複制到站點上或其他項目中的其他腳本中。

當隻是把函數放入一個已經建立的腳本中時,會産生一個潛在的問題。如果腳本使用和函數同樣的變量名,會發生什麼情況呢?函數會覆寫腳本中的變量嗎?或者情況剛好相反?例如:

注意,變量message既出現在函數之外(腳本的第一行代碼),也作為函數中的一個參數。參數實際上是在函數調用的時候才會使用資料填充的一個變量。在這個例子中,函數調用warning('inside the function')給函數傳遞了一個字元串參數,并且函數把這個字元串存儲到message變量中。這看上去似乎message變量有兩個版本。那麼,在腳本的第一行建立的最初的message變量中的值發生了什麼?

你可能會認為存儲在message中的最初的值被一個新值覆寫了,這個新值就是字元串“outside the function”,但實際上不是這樣。當運作這段腳本的時候,會看到兩個對話框:第一個顯示“inside the function”,而第二個顯示“outside the function”。實際上有兩個名為message的變量,但是,它們存在于不同的位置(如圖3-8所示)。

《JavaScript和jQuery實戰手冊(原書第2版)》——3.4節函數:把有用的代碼轉換為可複用的指令

javascript變量對于在函數内部聲明的變量和在函數外部建立的變量差別對待。用程式設計的術語來說,每個變量都有自己的作用域。函數的作用域就像是包圍起函數的一堵牆,牆内部的變量不會被牆外部的其他腳本所看到。當你第一次學習作用域的時候,它确實是一個容易混淆的概念,但是它非常有用。因為函數有自己的作用域,是以不必擔心在函數中為參數使用的名字會覆寫在腳本其他部分所使用的變量或與之發生沖突。

到目前為止,我們隻是讨論了變量作為參數使用的情況。但是,對于在函數内部建立的但不是用作參數的變量,會怎麼樣呢?例如:

這段代碼建立了message變量兩次,在腳本的第一行建立一次,在函數内的第一行再建立一次。這種情況和使用它作為參數的情況相同,隻不過在函數中輸入了var message,我們在函數的作用域内建立了一個新的變量。這種類型的變量叫做局部變量(local variable),既然它隻在函數的圍牆之内可見,主腳本和其他函數是不能看到或通路這個變量的。

然而,在腳本的主要部分(函數之外)建立的變量則存在于全局作用域内。腳本中所有的函數都可以通路在其主體内建立的變量。例如,在下面的代碼中,變量message建立于腳本的第一行,它是一個全局變量,并且它可以被函數通路。

這個函數沒有任何參數,并且沒有定義一個message變量,是以,當alert(message)部分運作的時候,函數查找一個名為message的全局變量。在這個例子中,該變量存在,是以顯示一個帶有文本“global variable”的警告框。

關于局部變量和全局變量,有一個潛在的陷阱,如果一個變量是參數,或者如果一個變量通過var關鍵字建立于函數的内部,它隻存在于函數的作用域中。圖3-9說明了這種情況。代碼的上半部分展示了一個名為message的全局變量和一個名為message的函數局部變量,二者可以并列存在。關鍵在于,函數中的第一行代碼var message ='inside the function';。通過使用var,我們建立了一個局部變量。

把這個和圖3-9下半部分的代碼相比較。在下半部分代碼中,函數沒有使用var關鍵字。相反,代碼行message='inside the function';沒有建立一個新的局部變量;它直接把一個新值存儲到全局變量message中。結果呢?這個函數幹擾了全局變量,替換掉了其初始值。

《JavaScript和jQuery實戰手冊(原書第2版)》——3.4節函數:把有用的代碼轉換為可複用的指令

變量作用域的概念确實很容易令人混淆,是以,現在提前讨論這個問題對我們沒有太多意義。但是,請記住,如果在腳本中建立的變量似乎沒有存儲我們期望的值,可能是遇到了作用域問題。如果遇到這種情況,可以傳回來閱讀這一節。

繼續閱讀