類函數是在類中被原型化的函數。每個應用中的函數都會執行特定的任務,例如解析一個 email 地址或者從配置文件讀取數據。在下一章中將要學習的,這些作為類函數存在的函數,通常執行和父類相關的一些處理。所有類函數都有如下結構:
~~~
[public/private] [static] function <name>( param1:paramType [, param2 : paramType], ...]):returnType
{
//...
}
~~~
`public`,`private` 和 `static` 標識符在下一章中討論,所以現在可以跳過它們。接下來是 `function` 關鍵字,表示接下來要聲明一個函數。函數的名字會緊跟 `function` 關鍵字,和 Haxe 變量具有相同的命名規則。盡管一個函數取什么名字是你的權利,但通常在 Haxe 的命名約定中,函數名總是以一個小寫字母開始。
函數名后是聲明參數的部分,使用圓括號包圍。這些參數和聲明局部變量的方式相似,不用使用 `var` 關鍵字,并且遵循相同的類型推斷規則。可以使用任意希望的參數數量,盡可能的少一些,易于使用。當你的函數被調用,值會通過函數調用傳遞到函數中,對應函數原型中提供的參數,必須是相同類型或者參數的擴展類型。然后函數中可以通過訪問他們和參數名使用這些值,因為它們可能是任何變量。
函數的最后一部分是返回值。返回值是所有必須的處理結束之后從函數返回數據的值。如果沒有值被返回,類型定義必須是Void 。這是一個簡單的函數:
~~~
public static function main()
{
var i : Int = 0;
for ( j in 1...10 )
{
i = add( i, j );
trace( i );
}
}
public static function add( num : Int, numToAdd : Int ) : Int
{
num += numToAdd;
return num;
}
~~~
這里,函數 `add` 添加值 `numToAdd` 到值 `num` 。當循環第一次迭代,`j` 的值是 `1` ,被添加到變量 `i` 。在隨后的迭代, `j` 的值在添加到變量 `i` 之前 遞增 `1` ,所以 `i` 的值包含和每次迭代都是一個三角數。如果運行這個例子,會被呈現的值是 1,3,6,10,15,21,28,36 和 45 。
# 從函數返回
* * * * *
`return` 關鍵字允許一個函數在函數體的任何地方結束。例如,如果你希望提前退出一個函數,可以使用 `return` 關鍵字到希望函數退出的地方,然后其余的函數代碼就會被跳過:
~~~
public static function someFunction()
{
// do code
if ( i < 20 ) return;
// more code
}
~~~
這里,模擬的函數 `someFunction` 會在變量 `i` 小于 `20` 的時候退出。
使用 `return` 關鍵字,還可以使函數返回一個值,只需要把值放到 `return` 關鍵字之后:
~~~
public static function someFunction() : Int
{
// do code
if ( i < 20 )
return i;
// more code
}
~~~
這里,`i` 的值被從函數退出時返回。
沒有返回值的函數,實際上返回 `Void` 。
# 函數類型推斷
* * * * *
分配給每個函數參數的類型不需要被指定,但是會被編譯器通過類型推斷設置。這意味著參數被推斷為在一個值被傳遞到它之前沒有類型。一旦傳遞了值,參數會被設定為值的類型然后繼續向前。
由第一次被傳遞到函數的值的類型設定參數類型,這可能引起一個小問題,例如:
~~~
public static function main()
{
display( 2 );
display( 2.5 );
}
public static function display( num )
{
trace( num );
}
~~~
這里的 `display` 函數幾乎是完全沒有用場的函數,但是它能說明一個問題。你知道類型 `Int` 是一個 `Float` 的擴展,所以一個參數類型為 `Float`,可以接受其它類型,但是如果第一次傳遞一個 `Int` 類型到函數,參數會被類型化為 `Int`,所以 `Float` 就不能再被接受。唯一安全的方式是添加 `Float` 類型到 `num` 參數原型,而不是依賴類型推斷。
# 動態函數
* * * * *
使用 `Dynamic` 類型,可以使用實際類型本身適用的一些處理。許多處理可以應用到許多數據類型的值。例如,加法運算符(`+`)可以用在 Float,Int 和 String 類型。對于 Float 和 Int,加法運算符把值加到一起,而用在一對字符串,則會連接它們。這意味著下面的例子是完全有效的:
~~~
public static function main()
{
// Outputs: 4
trace( double( 2 ) );
// Outputs: 5
trace( double( 2.5 ) );
// Outputs: haXehaXe
trace( double( “haXe” ) );
}
public static function double( num : Dynamic ) : Dynamic
{
return num + num;
}
~~~
當使用 Std.is() 函數,它應該能為一些不太適用某些處理的類型提供替代函數。例如,可以重寫上面的例子:
~~~
public static function main()
{
// Outputs: 4
trace( double( 2 ) );
// Outputs: 5
trace( double( 2.5 ) );
// Outputs: haXe haXe
trace( double( “haXe” ) );
// Outputs: [ 1, 2, 3, 1, 2, 3 ]
trace( double( [ 1, 2, 3 ] ) );
// Outputs: null
trace( double( { param : “value” } ) );
}
public static function double( val:Dynamic )
{
if( Std.is( val, Int ) || Std.is( val, Float ) )
return val + val;
else if( Std.is( val, String ) )
return val + " : " + val;
else if( Std.is( val, Array ) )
return val.concat( val );
else
return null;
}
~~~
# 遞歸函數
* * * * *
一個遞歸函數,是指調用它本身的函數。在這樣做時,函數創建一個循環,類似 `while` 和 `for` 循環,需要設置某些條件來結束遞歸。遞歸的好處是,你可以在代碼中更好的控制循環,并且只需要一個層次的功能而不是兩個更多的循環嵌套函數。
遞歸最大的好處,是函數調用為每個迭代返回值。這如果沒有一些類型的視覺幫助比較難以解釋,所以看下圖:

如你所見,正常的循環,你開始設置一組值,然后每個循環發生時,值被通過某些方式修改,從循環體的代碼創建一個稍有不同的結果。你可以使用遞歸函數模仿它,同樣,但是真正的能力來自它們返回的值。每次調用函數都被前一個調用處理,所以實際上調用時嵌套的。而使用遞歸,執行點把自己埋進每一個新的函數調用。然后,當遞歸最終結束,外部的函數調用執行點都返回一個值。這使循環沿著一個方向,然后轉進下一個。
看一下這幾個例子。首先你可以模仿一個 `while` 循環的效果:
~~~
public static function main()
{
var i = 0;
i = loop( i );
trace( i ); // Outputs: 20
}
public static function loop(num:Int):Int
{
return if( num<20 )
loop( num + 1 );
else
num;
}
~~~
當執行時,`loop` 函數以一個類似 `while` 循環的方式執行,例外是 `loop` 函數返回值代表迭代器迭代的次數。`loop` 循環也傳遞每個遞歸的返回值回傳給 `loop` 函數,雖然在這個例子中不是特別明白。要查看這個動作,最好提供代碼來跟蹤調用遞歸前后:
~~~
public static function main()
{
var i = 0;
i = loop( i );
trace( i ); // Outputs: 0
}
public static function loop( num : Int ) : Int
{
var ret = 0;
return if ( num < 6 )
{
trace( num );
ret = loop( num + 1 );
trace( ret );
--ret;
} else num;
}
~~~
在這個例子中,第一次傳遞到 `loop` 函數的值每次調用都會遞增。因為遞歸調用在每個 `loop` 函數中發生,每個調用在這個點結束執行,而新的調用發生。然后,遞增值達到 `6` ,所以 `loop` 循環不再遞歸調用,而最后傳遞的值被返回。這然后對于每個父循環調用有一個撞擊,在傳遞它回去之前遞減這個值。結果顯示為 0,1,2,3,4,5,6,5,4,3,2,1,0 。
- 本書目錄
- 第一章:Haxe介紹
- 互聯網開發的一個問題
- Haxe是什么,為什么產生
- Haxe編譯工具
- Haxe語言
- Haxe如何工作
- 那么Neko是什么
- Haxe和Neko的必須條件
- 本章摘要
- 第二章:安裝、使用Haxe和Neko
- 安裝Haxe
- 使用Haxe安裝程序
- 在Windows上手動安裝Haxe
- Linux上手動安裝Haxe
- 安裝Neko
- Windows上手動安裝Neko
- 在Linux上安裝Neko
- Hello world! 一式三份
- 編譯你的第一個Haxe應用
- 你的程序如何編譯
- HXML編譯文件
- 編譯到Neko
- 編譯為JavaScript
- 程序結構
- 編譯工具開關
- 本章摘要
- 第三章:基礎知識學習
- Haxe層級結構
- 標準數據類型
- 變量
- 類型推斷
- 常數變量
- 簡單的值類型
- 浮點類型
- 整型
- 選擇數值類型
- 布爾類型
- 字符串類型
- 抽象類型
- Void 和 Null
- 動態類型
- unknown類型
- 使用untyped繞過靜態類型
- 注釋代碼
- 轉換數據類型
- Haxe數組
- Array
- List
- Map
- Haxe中使用日期時間
- 創建一個時間對象
- Date組件
- DateTools類
- 操作數據
- 操作符
- Math類
- 使用String函數
- 本章摘要
- 第四章:信息流控制
- 數據存放之外
- 條件語句
- if語句
- switch語句
- 從條件語句返回值
- 循環
- while循環
- for循環
- 循環集合
- Break和Continue
- 函數
- 類的函數
- 局部函數
- Lambda類
- 本章摘要
- 第五章:深入面向對象編程
- 類和對象
- 實例字段
- 靜態字段
- 理解繼承
- Super
- 函數重載
- 構造器重載
- toString()
- 抽象類和抽象方法
- 靜態字段,實例變量和繼承
- 繼承規則
- 使用接口
- 高級類和對象特性
- 類的實現
- 類型參數
- 匿名對象
- 實現動態
- Typedef
- 擴展
- 枚舉
- 構造器參數
- 本章摘要
- 第六章:組織你的代碼
- 編寫可重用代碼
- 使用包
- 聲明一個包
- 隱式導入
- 顯式導入
- 枚舉和包
- 類型查找順序
- 導入一個完整的包
- 導入庫
- Haxe標準庫
- Haxelib庫
- 其他項目中的庫
- 外部庫
- 使用資源
- 文檔化代碼
- 離線文檔
- 在線文檔
- 單元測試
- haxe.unit包
- 編寫測試
- 本章摘要
- 第七章:錯誤調試
- trace函數
- trace輸出
- haxe的trace和ActionScript的trace
- 異常
- 異常處理
- CallStack和ExceptionStack
- 異常管理類
- 創建完全的異常處理類
- 異常類代碼
- 本章摘要
- 第八章:跨平臺工具
- XML
- XML剖析
- Haxe XML API
- 正則表達式
- EReg類
- 模式
- 定時器
- 延遲動作
- 隊列動作
- MD5
- 本章摘要
- 第九章:使用Haxe構建網站
- Web開發介紹
- Web 服務器
- 使用Web服務器發布內容
- HTML速成課程
- Haxe和HTML的區別
- NekoTools Web Server
- Apache安裝mod_neko
- Windows安裝Apache和mod_neko
- Linux安裝Apache和Mod_Neko
- 第一個Haxe網站
- 使用Neko作為網頁Controller
- neko.Web類
- Neko作為前端控制器
- 本章摘要
- 第十章:使用模板進行分離式設計
- 什么是模板
- Template類
- Template語法
- 使用資產
- 何時在模板中使用代碼
- 服務器端模板的Templo
- 安裝Templo
- 使用Templo
- haxe.Template和mtwin.Templo表達式上的區別
- Attr表達式
- Raw表達式
- 邏輯表達式
- 循環表達式
- set, fill, 和 use表達式
- Templo中使用宏
- 手動編譯模版
- 第十一章:執行服務端技巧
- 第十二章:使用Flash構建交互內容
- 第十三章:使用IDE
- 第十四章:通過JavaScript制作更多交互內容
- 第十五章:通過Haxe遠程通信連接所學
- 第十六章:Haxe高級話題
- 第十七章:Neko開發桌面應用
- 第十八章:用SWHX開發桌面Flash
- 第十九章:多媒體和Neko
- 第二十章:使用C/C++擴展Haxe
- 附加部分