我們之前把函數看做是代碼的邏輯單元,實際上不僅如此,函數還和我們用var或let定義的東西一樣,可以被賦值并且有類型呢!怎么樣?是不是感覺函數這家伙看似忠厚老實,其實背后還有故事?確實,函數背后面還有大大的故事呢。
1、函數是個什么
func printStr(str: String) {
print(str)
}
let anotherPrintStr = printStr
anotherPrintStr("Hello world") // 在控制臺輸出 "Hello world"
我們首先定義了一個函數 printStr 接著我們把它賦值給了一個用let聲明的常量 anotherPrintStr,然后,然后,然后 anotherPrintStr 居然可以被當做一個函數進行調用了。這篇代碼看起來還是不夠明顯,讓我們繼續研究一下,我們應該還記得我們之前提前過的 類型推斷 機制吧,既然swift是一們靜態的強類型語言,那么它是不會允許常量 anotherPrintStr 沒有類型而存在,此時一定是類型推斷機制在暗中給 anotherPrintStr 賦予了類型,我們可以通過xcode幫助我們揪出這個類型是什么,這個類型就是: (String) -> Void
函數的類型是由 參數類型與返回值類型共同決定的,Void代表函數沒有返回值,如果一個函數既沒有參數也沒有返回值,那么該函數的類型應該是 () -> Void
那么也就是說我們可以用顯示聲明的方式來定義anotherPrintStr了。
let anotherPrintStr: (String) -> Void = printStr
anotherPrintStr("Hello world") // 在控制臺輸出 "Hello world"
這個時候我們已經完全明白anotherPrintStr不過是一個函數類型的常量嗎,但是函數printStr到底是什么呢?我們嘗試用函數類型重新定義一下printStr。
let printStr: (String) -> Void = {str in
print(str)
}
printStr("Hello world") // 在控制臺輸出 "Hello world"
通過這段代碼我們可以這樣理解printStr,它是一個用let聲明的常量,類型是函數類型 (String) -> Void 它的值是一段可以執行的代碼體(這是閉包的定義方式,我們可以暫時忽略)。也就是說和一個整形常量的區別在于,printStr的值可以被執行。
2、函數是一類公民
我們可能早就聽說了,swift的函數是一類公民,這其實說的是函數與swift的其他類型一樣,并沒有受到歧視,而是得到了平等的對待,這包括賦值,傳遞,作為參數和返回值等。
func printStr(str: String) {
print(str)
}
func anewFunc(perform: (String) -> Void) -> (String) -> Void {
return perform
}
anewFunc(printStr)
我們定義了一個函數 anewFunc 它接收一個 (String) -> Void 類型,并返回一個 (String) -> Void 類型。我們可以把函數作為參數傳遞給 anewFunc ,而該函數又將其做為返回值。
3、這一切都是為了啥
我們現在承認 函數確實能做為另外一個函數的參數和返回值,可是這折騰來折騰去的是為了啥呢?我想這應該有兩點原因。
寫代碼的一個重要目標是提高代碼的復用度,這同時也能標識一個程序員水平的高低,編程語言提供的抽象表達能力,以及我們經常討論的各種設計模式都是在為了實現這一目標,如果函數能夠成為一等公民,那么意味著我們將獲得一種將模板與代碼行為分離的表達能力,這是一種很強的抽象表達能力,可以有效的提高代碼的復用度可以看另外一篇內容來介紹閉包)。
所有我們聲明的內容,都有其作用域和聲明周期,如果我們想打破其作用域和聲明周期,那么我們可以使用return語句或者是閉包(可以看另外一篇內容來介紹swift的作用域)。