昨天的例子中,我們提到了echo
這個函式,明明宣告時我們只指定了一個參數,但是我們在呼叫時卻可以傳入一個以上的引數,而JavaScript都不會報錯(雖不合理但合法),只有在TypeScript檢查後才會報錯。這個現象,剛好可以帶到今天要講的題目:函式多載(function overloads)
函式多載這個概念,在JavaScript當中其實並不存在,他只出現在例如:C++、C#、Java、Swift、Kotlin 等語言當中。簡單來說,函式多載允許同名的函式,能夠有不同的參數數量、不同的實踐內容。我們來看JavaScript當中,如果想要做到其他語言的函式多載,會發生什麼事。
function echo(msg) {
console.log(msg)
}
function echo(msg1, msg2) {
console.log(msg1, msg2)
}
echo("Hi there")
//Hi there undefined
在這個情況中,我們宣告了兩次echo
,而後者會覆蓋掉前者,所以我們沒辦法只帶一個引數給echo
(以這狀況其實是可以啦,只是結果是不是你與其他使用者共同預期的,就不得而知了)。如果是支援函式多載的程式語言,他就會根據你的引數數量的不同,去決定要執行的是哪個版本的函式。
那我們來看看TypeScript要怎麼達到函式多載吧:
interface Player {
name: string
height: number
}
//下面這兩行,我們宣告了兩種不同參數數量的函式
//並在後面根據引數數量的不同,去執行其內容
//這兩行叫做overload signature,可以想成
function playerSpike(name: string, height: number): void
function playerSpike(playerObject: Player): void
//由於我們有1個跟2個參數playerSpike,所以第二個參數必須是optional
function playerSpike(arg1: unknown, arg2?: unknown): void {
//接下來我們實作第一種需要兩個引數的函式
if (typeof arg1 === "string" && typeof arg2 === "number") {
console.log(`${arg1} is ${arg2}cm tall`)
}
//以及只有一種引數的函式
if (typeof arg1 === "object") {
const { name, height } = arg1 as Player
console.log(`${name} is ${height}cm tall`)
}
}
playerSpike("John", 180)
playerSpike({ name: "Allen", height: 190 })
你如果問這有什麼好處,一方面是讓從其他語言跳過來寫JavaScript的人能更快上手,不被"沒有函式多載“的特性給衝康,另一方面就是我們能用同一個名稱的函式,做各式各樣的事情,而不用定義多個函式。
而且,我們的編輯器在我們輸入完playerSpike()
以後,他會告訴我們:我們有兩種playerSpike可以用,一種是接收兩個引數的,一種只接收一個型別為Player
的引數!
所以,方便歸方便,但對於原本就在寫JavaScript的人(我)來講,其實是有一點煩瑣的,所以我目前即便知道有這個概念,但還沒找到適合實作的地方,就給大家參考囉。
replaceAll('型別多載', '函式多載') //XD
這種表示方式我一直很好奇也很有興趣
既然 JavaScript 當中沒有函式多載的概念,那像是 Array.prototype.map()
是怎麼做到代入不同數量的引數,執行不同的結果的呢?
感謝提醒,已修改!
我寫這篇時一直想到型別多載,但明明就是函式多載~"~
因應你的問題(也是這系列文第一個留言的QQ),我還特地去翻了一下es5的文件
簡單結論是:他跟函式多載沒什麼關係,也用不到函式多載。
以下是我的想法,歡迎討論&鞭:
由於Array.prototype中各種遍歷的方法的callback
函式都是我們提供的
所以以Array.prototype.map(function(a,b,c){})
來舉例,function(a,b,c){}
當中a
是必須,且使用者有提供,那就把現在在遍歷的那個"值"給帶進去a
。
如果b
有提供,那就把正在遍歷的那個值的"索引值"帶入b
如果c
有提供,那就把正在遍歷的那個"陣列"給帶入c
所以我猜就只要透過單純的判斷式,可能再給b
跟c
一個預設值,就能讓callback去使用不同數量的引數。
感謝,努力消化