函数重载
函数重载适用完成某种功能相同但细节又有不同的场景
比如说吃饭是一个函数,但是西方人用叉子,东方人用筷子,细节有所不同,这种场景就比较适合用函数重载。
需求场景举例:
假设服务端以数组的形式返回了一组数据,我们需要通过传入的参数从数组中查找对应数据,如果入参为数字, 就认为消息 id,然后从从后端数据源中找对应 id 的数据并返回,否则当成类型,返回这一类型的全部消息。
type MessageType = "image" | "audio" | string;//微信消息类型
type Message = {
id: number;
type: MessageType;
sendmessage: string;
};
let messages: Message[] = [
{
id: 1, type: 'image', sendmessage: "你好啊,今晚咱们一起去三里屯吧",
},
{
id: 2, type: 'audio', sendmessage: "朝辞白帝彩云间,千里江陵一日还"
},
{
id: 3, type: 'audio', sendmessage: "你好!张无忌"
},
{
id: 4, type: 'image', sendmessage: "刘老根苦练舞台绝技!"
},
{
id: 5, type: 'image', sendmessage: "今晚王牌对王牌节目咋样?"
}]
非函数重载的代码实现
因为我们传入的值有两种可能性,所以入参value的值是两种类型的或的关系,之后再用类型窄化做具体判断。
function getMessage(value: number | MessageType): Message | undefined | Array<Message> {
if (typeof value === "number") {
return messages.find((msg) => value === msg.id )
} else {
//return messages.filter((msg) => { return value === msg.type })
return messages.filter((msg) => value === msg.type)
}
}
console.log(getMessage("audio"));
console.log(getMessage(1));
函数重载代码实现
非函数重载的方案有一个不太好的地方在于函数阅读体验不好,扩展性略差。如果只看value: number | MessageType并不能比较直观的反映出我们其实是有两种类型的功能。下面看一下函数重载实现方案。
//第一个根据数字id来查询单个消息的重载签名
function getMessage(value: number): Message
//第二个根据消息类型来查询消息数组的重载签名
function getMessage(value: MessageType): Message[]
function getMessage(value: any) {
//console.log(myname)
if (typeof value === "number") {
return messages.find((msg) => { return value === msg.id })
} else {
return messages.filter((msg) => value === msg.type)
}
}
看起来就简洁明了很多,看重载签名就可以知道它可以实现的两个具体功能。
函数重载由一个函数签名以及一到多个重载签名组成。但外部调用函数时,只会调用重载签名,不会调用实现签名。实现签名只在定义时起到统领所有重载签名的作用。当我们在调用函数的时候,系统会通过传递的参数自动判断该使用哪一个重载签名。
另外,虽然实现签名只是起到统领的作用,但是具体函数里面的变量是要到实现签名里面去取值的。所以,如果函数里某个变量被用到,但是该变量只在重载签名里写而不在实现签名里去写,会报错。实现签名既然要统领重载签名,最好足够统领会比较好。可以看下面的例子:
function getMessage(value: number, test: number): Message
function getMessage(value: MessageType): Message[];
//function getMessage(value: any, test: any=1) {
function getMessage(value: any) {
if (typeof value === "number") {
const haha = test
return messages.find((msg) => { return value === msg.id })
} else {
return messages.filter((msg) => value === msg.type)
}
}
只在重载签名中声明test,而不在实现签名中声明test,实现签名里面的变量需要test会从实现签名中去找,没有找到因此报错。
必须给重载签名提供返回值类型,TS 无法默认推导。
再来演示一下函数重载如何扩展。假设有新的需求,如果消息类型是MessageType时,希望不要把数据全部返回,而是只返回前两条的数据,我们可以这样写第二条重载签名:
function getMessage(value: number): any
function getMessage(value: MessageType, readRecordCount: number): Message[]
function getMessage(value: any, value2: any = 1) {
//console.log(myname)
if (typeof value === "number") {
return messages.find((msg) => { return value === msg.id })
} else {
return messages.filter((msg) => value === msg.type).splice(0, value2)
}
}
console.log(getMessage(1))
console.log(getMessage("image", 2))
方法重载
在对象中使用函数称之为方法,所以方法是特殊的函数,具体规则形式是差不多的。下面举一个方法重载的例子,对remove方法做重载,可以返回被删除的元素或者具体对象:
class ArrayList {
//第一步:定义一个引用属性【数组】
constructor(public element: Array<object>) {
}
// 第二步:根据索引来查询数组中指定元素
get(index: number) {
return this.element[index]
}
// 第三步: 显示方法
show() {
this.element.forEach((ele) => {
console.log(ele);
})
}
remove(value: number): number
remove(value: object): object
remove(value: any): any {
this.element = this.element.filter((ele, index) => {
//如果是根据数字【元素索引】去删除元素,remove方法返回的是一个数字
if (typeof value === "number") {
return value !== index
} else {
// 如果是根据对象去删除元素,remove方法返回的是一个对象
return value !== ele
}
})
return value;
}
}
let stuOne = { stuname: "wnagwu", age: 23 }
let stuTwo = { stuname: "lisi", age: 39 }
let stuThree = { stuname: "liuqi", age: 31 }
let arrayList = new ArrayList([stuOne, stuTwo, stuThree]);
arrayList.show();
// let value = arrayList.remove(0)
// console.log("删除的元素为第:", value, "学生")
// arrayList.show();
let value = arrayList.remove(stuTwo)
console.log("删除的学生对象为:", value)
arrayList.show();