張智星 jang@mirlab.org http://mirlab.org/jang 台大資工系 多媒體檢索實驗室 第五章 自訂函數 張智星 jang@mirlab.org http://mirlab.org/jang 台大資工系 多媒體檢索實驗室
本章大綱 大綱 本章說明 JavaScript 的函數如何使用,包含在函數內外的變數有效範圍、如何將函數集中於函數定義檔、如何產生遞迴函數、如何產生具有可變個數之輸入的函數,以及如何預約及取消程式碼的執行。精通函數的使用,會使程式碼的組合更具有彈性、管理更加便利。 主題 5-1:變數有效範圍 5-2:程式定義檔案的使用 5-3:遞迴呼叫與可變個數的引數 5-4:預約程式碼的執行
5-1:變數有效範圍 本小節介紹變數在函數中的有效範圍。
函數的分類 「函數」(Functions)是所有的程式語言必備的功能。JavaScript 的函數可以分成下列幾類 內建函數:又分成兩類 一般內建函數,如 parseInt() 和 parseFloat() 等。 與物件結合的函數,例如 Math.sin() 或 string.big()等,這類的函數又稱為物件的方法(Method)。 使用者定義的函數:也可以分一般函數與物件的方法兩類。 一般而言,我們使用「函數」來代表數學函數,例如三角函數等,而用「函式」來代表程式碼中的函數。但在本書中,並沒有此嚴謹的區分,因此「函式」和「函數」是可以交換使用,通常都是指程式碼所定義的函數。
自訂函式架構(1) 基本結構 說明 括號裡的輸入引數(Input Arguments),必須以逗號分開。 定義函數並不代表函數的執行,只有在程式中呼叫函數的名稱後,才會執行該函數。 若有需要,函數最後可用 return 來傳回輸出變數 (數值、字串,或其他型態的變數)至呼叫此函數的程式環境。 function functionName(InputArguments) { JavaScript statements ... return(output) // 可省略 }
自訂函式架構(2) 說明 一般網頁中的的事件處理器(Event handlers),通常都是以 JavaScript 或是 VBScript 的函數來描述。 函數的定義,通常寫在 <head> 及 </head> 之間,以確保 HTML 主體在被呈現前,所有相關的 JavaScript 函數都已被載入,並隨時可被執行。 一般來說,我們希望函數的定義出現的位置和它被呼叫之處能越接近越好,以方便程式管理,在這種情況下,只要函數定義出現在其被呼叫之前,大致上都不會有什麼問題。
範例5-1 主題:使用函數來顯示現在時間與星期幾 說明 連結:timeDisplay01.htm 程式碼重點 return後面的()可以省略。 程式碼中的var表示區域變數,後面有詳細介紹。 return(prepand+hour+”點”+minute+”分”+second+”秒");
區域變數和全域變數 區域及全域變數的概念: 區域(局部)變數 (Local variables): 只有在變數本身的函數裡才看的見的變數。欲定義局部變數,可在變數第一次使用時加上 var。 全域變數 (Global variables): 在整個程式設計的過程中都可以看的見、 而且每一個函數都可以用的變數。若不對變數做任何處理,JavaScript 的變數預設狀態即是全域變數。
範例5-2 主題:展示變數的有效範圍 說明 連結:scopeOfVariable01.htm 程式碼重點 函數內的變數x和外面的x雖然名稱一樣,但是用var宣告,執行函數後外面的變數x值不受影響。 函數內的變數y和函數外面y的名稱一樣,且執行函數完後,外面的變數y會變成5。 為了減少除錯的時間,所有函數的內部變數,在第一次使用時最好加上 var,已確認其有效範圍只在此函數內。 function內: var x=5; // 局部變數 y=5; // 全域變數 function外: x = 10; // 全域變數 y = 10; // 全域變數
5-2:函數定義檔案的使用 本小節介紹如何使用函數定義檔,使得JavaScript程式更方便管理。
函數定義檔案 基本結構 說明 上述範例中的函數定義檔的副檔名是 “js”,這只是一般習慣,使用者也可以選用其它任意附檔名。 若找不到檔案,網頁並不會印出錯誤訊息。 使用函數定義檔的好處: 我們只要寫一個包含函數定義的 檔案,就可以被不同的網頁所使用,非常方便。 函數定義檔也可以用在伺服器端或本機端的程式碼,一魚三吃。 <script src="函數定義檔案.js"></script>
範例5-3 主題:使用函數定義檔案來顯示現在時間 說明 連結:timeDisplay02.htm 程式碼重點 範例中,我們交互運用雙引號 (“) 及單引號 (‘),以明白區分 href 與 JavaScript 所用的字串。 重點程式碼中包含的.js檔,在下一個範例會說明。 <script src="time.js"></script>
範例5-4 主題:與時間相關的函數定義檔 說明 連結: time.js 寫成一個檔案時,不需要加上<script></script>這兩個標籤,也不能包含其他HTML標籤。 有關於時間和日期的用法,請見前幾章的說明。 副檔名可以任意取,例如 time.fun,請見此範例 timeDisplay03.htm
範例5-5 主題: 使用函數定義檔寫出“document” 的屬性列表 說明 連結: docProp01.htm 程式碼重點 listProp中,第一個參數放的是物件,第二個是字串(做為此物件的名稱)。 listProp.js這個檔案在下一個範例會說明。 <script src="listProp.js"></script> <script>listProp(document, "document")</script>
範例5-6 主題:建立物件屬性列表的函數定義檔 說明 連結: listProp.js 程式碼 範例中的 for-in 迴圈特別適用於列出一個物件的所有屬性。 (有關於文件的屬性,請見下一章的說明。) function listProp(obj, objName) { for (var i in obj) document.writeln(objName+".<font color=red>"+i+"</font> = <font color=green>"+obj[i]+"</font><br>"); }
5-3:遞迴呼叫與可變個數的引數 本小節介紹遞迴函數(recursive function)的概念與使用,以及可變個數引數(Input Arguments of Variable Length)的函數使用。
遞迴函數概念 基本結構 說明 一個函數可以呼叫自己,直到某個條件達成時,才停止呼叫自己,這樣的行為稱為遞迴呼叫。 如果沒有結束條件,這個函數就會永無止盡的呼叫,形成無窮遞迴。 function myFunc(n){ if(…) return(…); //結束條件 return(… myFunc(n-1) …); //遞迴呼叫 }
範例5-7 主題:使用遞迴方式進行階乘函數的計算 說明 連結: funcFact.htm 程式碼重點 遞迴函數易耗CPU及記憶體資源,在某些特殊演算法上程式碼比較精簡(例如河內塔)。 要理解遞迴的運作方式,最好的方式是沿著程式碼人工執行一次。 function fact(n){ // 以遞迴方式進行階乘函數的計算 if (n==0) return(1); // 結束條件 return(n*fact(n-1)); // 遞迴呼叫 }
可變個數的輸入引數 基本結構 說明 引數本身是一個陣列,陣列變數為myFunc.arguments。 myFunc.arguments.length 代表真正的引數個數。 JavaScript和C語言不同(一般函數而言),它在呼叫函數時,如果超過函數定義的引數個數時,仍然可執行,而C語言必須要將函數宣告成可變長度才能使用。 function myFunc(arg1, arg2){ … x = myFunc.arguments[2]; //第三個引數 … }
範例5-8 主題:可變引數個數的函式範例 說明 連結: list.js 程式碼 type如果是“o”表示有序列表。 type如果是“u”表示無序列表,後面範例會詳述。 function list(type) { document.write("<" + type + "l>"); for (var i=1; i<list.arguments.length; i++) document.write("<li>" + list.arguments[i]); document.write("</" + type + "l>"); }
範例5-9 主題:使用list.js展示有號及無號列表的可變長度引數個數的函數 說明 連結: list01.htm 程式碼重點 <script src="list.js"></script> <script>list("u", "香蕉", "橘子", "蘋果"); </script> <script>list("o","冠軍","亞軍","季軍","墊底"); </script>
5-4:預約程式碼的執行 本小節介紹setTimeout()及setInterval()兩種預約程式碼的函式使用。
setTimeout()使用方式 基本結構 說明 這個函式的目的是使某段程式碼在指定時間後開始執行。 第一個參數放要執行的程式碼,可以是一個函數,或者是一段JavaScript程式碼,以字串的形態傳入。 第二個參數放的是幾秒後開始,單位是毫秒(ms),所以要用秒來計算時記得”*1000”。 用clearTimeout(timer)可以取消預約。 timer = setTimeout("JavaScript的命令列", 時間長度);
範例5-10 主題:使用setTimeout()函式執行預約程式碼 說明 連結: setTimeout01.htm 程式碼重點 要修改按鈕顯示的文字,可以修改document物件的表單theForm裡面的theButton。 JavaScript程式碼如果寫在同一行要用”;”分隔,如果是最後一個敘述,”;”可以省略。 setTimeout("alert('轟!!!'); document.theForm.theButton.value='丟手榴彈'", 3*1000);
範例5-11 主題:利用setTimeout()反覆執行某段程式碼 說明 連結: movingText01.htm 程式碼重點 id和name屬性有些微的不同,id就像全域變數一樣,在JavaScript中可以直接呼叫,但是最保險的方式是用:document.getElementById(“標籤id”); 當函式執行到setTimeout的時候會預約下一次的執行,即可定時重複執行這個函式。 function showMovingText(){ setTimeout("showMovingText()", 400); }
範例5-12 主題:使用clearTimeout()來取消預約程式碼 說明 連結: setTimeout02.htm 程式碼重點 若按下「開始計時」,我們使用程式碼重點第一行,來預約 showTime() 在每 0.1 秒執行一次。 若按下「取消計時」,則我們使用第二行,來取消已經預約的程式碼。 timer=setTimeout("showTime();", timeStep*1000); clearTimeout(timer);
setInterval()使用方法 基本結構 說明 和setTimeout()格式相同,功能是每固定時間執行某段程式碼。 也可以用setTimeout()達到此效果,但是setInterval()更為精簡。 用clearInterval()可以將回傳的timer取消預約。 如果時間長度越短,重複執行的時間誤差越大。 timer = setInterval("JavaScript的命令列", 時間長度)
範例5-13 主題:使用setInterval()更新目前時間 說明 連結:setInterval01.htm 程式碼重點 每次執行showTime()都要宣告一個Date物件,如果使用同一個Date物件,目前時間的數據是不會更新的。 date=new Date(); document.theForm.theText.value=date.toLocaleString(); setInterval("showTime()", 1000);
範例5-13 主題:使用clearInterval()取修預約程式碼 說明 連結:clearInterval01.htm 程式碼重點 這只是一個簡單範例,所以你一旦停止時間顯示,就沒辦法再恢復,除非重新載入網頁。 clock=setInterval("showTime()", 1000); <input type=button name=controlButton value="停止時鐘" onClick="clearInterval(clock)">