深度掌握TypeScript中的重载【函数重载、方法重载】

1. 函数重载,方法重载的重要性

著名前端流行框架底层都用到函数重载,例如:Vue3 底层源码就多处使用到带泛型的函数重载。很多前端面试更是拿函数重载作为考核求职者 TS 技能是否扎实的标准之一,如果你不掌握函数重载,等于你的 TS 技能有缺失,技能不过关。

函数重载或方法重载适用于完成项目种某种相同功能但细节又不同的应用场景,我们举一个生活中的例子让同学们先有个印象,比如:吃饭是一个函数,表示一个吃饭功能,但西方人用叉子,中国人用筷子,这就是细节不同,那如果我们可以用函数重载来解决。

不管现阶段你公司的项目中是否用到了函数重载和方法重载【如果没有用,多半是公司不少人用的并不熟练才不用的缘故】。

函数重载或方法重载有以下几个优势:

优势1: 结构分明

让 代码可读性,可维护性提升许多,而且代码更漂亮。

优势2: 各司其职,自动提示方法和属性

每个重载签名函数完成各自功能,输出取值时不用强制转换就能出现自动提示,从而提高开发效率

优势3:更利于功能扩展

2.函数重载的定义

TS 的函数重载比较特殊,和很多其他后端语言的方法重载相比,多了不少规则。学习函数重载,先要了解什么是函数签名,定义如下:

函数签名 [ function signature ]:函数签名=函数名称+函数参数+函数参数类型+返回值类型四者合成。在 TS 函数重载中,包含了实现签名和重载签名,实现签名是一种函数签名,重载签名也是一种函数签名。

关于函数重载的定义,我们先来看一个很多其他资料提供的不完整且模糊的TS函数重载定义:

不完整模糊的 TS 函数重载定义:一组具有相同名字,不同参数列表的和返回值无关的函数 。

完整的函数重载定义:包含了以下规则的一组函数就是TS函数重载 :

规则1 由一个实现签名+ 一个或多个重载签名合成。

规则2 但外部调用函数重载定义的函数时,只能调用重载签名,不能调用实现签名,这看似矛盾的规则,其实 是TS 的规定:实现签名下的函数体是给重载签名编写的,实现签名只是在定义时起到了统领所有重载签名的作用,在执行调用时就看不到实现签名了。

规则3 调用重载函数时,会根据传递的参数来判断你调用的是哪一个函数

规则4 只有一个函数体,只有实现签名配备了函数体,所有的重载签名都只有签名,没有配备函数体。

规则5 关于参数类型规则完整总结如下:

实现签名参数个数可以少于重载签名的参数个数,但实现签名如果准备包含重载签名的某个位置的参数 ,那实现签名就必须兼容所有重载签名该位置的参数类型【联合类型或 any 或 unknown 类型的一种】。

规则6 关于重载签名和实现签名的返回值类型规则完整总结如下:

必须给重载签名提供返回值类型,TS 无法默认推导。

提供给重载签名的返回值类型不一定为其执行时的真实返回值类型,可以为重载签名提供真实返回值类型,也可以提供 void 或 unknown 或 any 类型,如果重载签名的返回值类型是 void 或 unknown 或 any 类型,那么将由实现签名来决定重载签名执行时的真实返回值类型。 当然为了调用时能有自动提示+可读性更好+避免可能出现了类型强制转换,强烈建议为重载签名提供真实返回值类型。

不管重载签名返回值类型是何种类型,实现签名都可以返回 any 类型 或 unknown类型,当然一般我们两者都不选择,让 TS 默认为实现签名自动推导返回值类型。

3.方法重载

方法:方法是一种特定场景下的函数,由对象变量【实例变量】直接调用的函数都是方法。

比如:

  1. 函数内部用 this 定义的函数是方法;

  2. TS 类中定义的函数是方法【 TS 类中定义的方法就是编译后 JS 底层 prototype 的一个函数】;

  3. 接口内部定义的函数是方法【注意:不是接口函数】;

  4. type 内部定义的函数是方法【注意:不是 type 函数】。

方法签名和函数签名一样,方法签名 = 方法名称 + 方法参数 + 方法参数类型 + 返回值类型四者合成。

4.构造器 【构造函数】重载

(1)再次强化this

this 其实是一个对象变量,当 new 出来一个对象时,构造器会隐式返回 this 给 new 对象等号左边的对象变量,this 和等号左边的对象变量都指向当前正创建的对象。

以后,哪一个对象调用 TS 类的方法,那么这个方法中的 this 都指向当前正使用的对象【 this 和当前的对象变量中都保存着当前对象的首地址】

(2)TS构造器没有返回值

尽管TS类构造器会隐式返回 this,如果我们非要返回一个值,TS 类构造器只允许返回 this,但构造器不需要返回值也能通过编译,更没有返回值类型之说,从这个意义上,TS 构造器可以说成是没有返回值这一说的构造函数。【注意:TS 构造器和 JS 构造函数关于返回值的说法不完全相同

(3)构造器不是方法

我们说对象调用的才是方法,但是 TS 构造器是在对象空间地址赋值给对象变量之前被调用,而不是用来被对象变量调用的,所以构造器( constructor )可以说成构造函数,但不能被看成是一个方法。

(4)构造器【构造函数】重载的意义

构造器重载和函数重载使基本相同,主要区别是:TS 类构造器重载签名和实现签名都不需要管理返回值,TS 构造器是在对象创建出来之后,但是还没有赋值给对象变量之前被执行,一般用来给对象属性赋值。

我们知道在 TS 类中只能定义一个构造器,但实际应用时,TS 类在创建对象时经常需要用到有多个构造器的场景,比如:我们计算一个正方形面积,创建正方形对象,可以给构造器传递宽和高,也可以给构造器传递一个包含了宽和高的形状参数对象,这样需要用构造器重载来解决。而面试中也多次出现过关于TS构造器重载的考察,主要考察求职者对重载+构造器的综合运用能力。

5.函数重载实战

场景:聊天应用中,发送的消息分为很多类型,例如:图片、文字、语音等等,现在需要封装一个消息查找函数,根据传入的参数从数组中查找数据,如果入参为数字, 就认为消息 id,然后从从后端数据源中找对应 id 的数据并返回,否则当成类型,返回这一类型的全部消息。

代码1,不使用函数重载的特性:

type MessageType = "image" | "audio" | string;//消息类型
//type xtype=string
//boolean true false
type Message = {
id: number;
type: MessageType;
sendmessage: string;
};
//let msgobj:Message={id:23,type:"df",sendmessage:"abc"}
//let obj={username:"wangwu",age:23}
let messages: Message[] = [
//let messages: Array<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: "今晚王牌对王牌节目咋样?"
}] //不用函数重载来实现
// 1.函数结构不分明,可读性,可维护性变差
function getMessage(value: number | MessageType):
Message | undefined | Array<Message> {
if (typeof value === "number") {
return messages.find((msg) => { return value === msg.id })
} else {
//return messages.filter((msg) => { return value === msg.type })
return messages.filter((msg) => value === msg.type)
}
}
// 自定义守卫
console.log(getMessage("audio"));
// TS没有办法运行之前根据传递的值来推导方法最终返回的数据的数据类型
// 只可以根据方法定义的类型展现
//let msg=getMessage(1)
//console.log(msg.sendMessage)//错误 类型“Message | Message[]”上不存在属性“sendMessage”。
// 类型“Message”上不存在属性“sendMessage”,联合类型使用方法,必须是交集(公共方法)!
let msg = (<Message>getMessage(1)).sendmessage // 类型转换成Message或类型断言
console.log("msg:", msg)// msg: 你好啊,今晚咱们一起去三里屯吧 export { }

代码2,使用函数重载实现:

type MessageType = "image" | "audio" | string;//消息类型

type Message = {
id: number;
type: MessageType;
sendmessage: string;
}; let messages: Message[] = [
//let messages: Array<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: "今晚王牌对王牌节目咋样?"
}] // function getMessage(value: number | MessageType):
// Message | undefined | Array<Message> {
// if (typeof value === "number") {
// return messages.find((msg) => { return value === msg.id })
// } else {
// //return messages.filter((msg) => { return value === msg.type })
// return messages.filter((msg) => value === msg.type)
// }
// }
// 重载签名可以有多个
function getMessage(value: number, myname: string): Message//第一个根据数字id来查询单个消息的 重载签名
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 6 === msg.id })//undefined
} else {
//return messages.filter((msg) => { return value === msg.type })
return messages.filter((msg) => value === msg.type).splice(0, value2)
}
}
getMessage(1, "df") // 这里调用的是第一个重载签名
// let x: number = 3;
// let y: any = x; // let z: any = 3;
// let k: number = z; // function go(value:number,readRecordCount:number=1){ // }
// go(1); //console.log(getMessage(6).sendmessage); 根据传参,判断为第一个重载签名,所以这里的sendmessage可以正常调用,不需要类型转换
getMessage("image", 2).forEach((msg) => {
console.log(msg);
}) export { }

6.方法重载实战

场景: Java 简易版 ArrayList 中的经典应用实现【 ArrayList 可弥补 Set 取值不方便短板,比 Set 删除功能更方便】

代码:

//  1.对现有的数组进行封装,让数组增删改变得更加好用
// 2.提供get方法 remove方法 显示方法【add方法】
// 其中需求中的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: number | object): number | 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(); console.log("删除第一个学生");
// let value = arrayList.remove(0)
// console.log("删除的元素为第:", value, "学生")
// arrayList.show();
let value = arrayList.remove(stuTwo)
console.log("删除的学生对象为:", value)
arrayList.show();
// 如果是根据数字【元素索引】去删除元素,remove方法返回的是一个数字
// 如果是根据对象去删除元素,remove方法返回的是一个对象
//let value=arr.remove(1)

7.构造器重载实战

场景:计算正方形面积

// 计算正方形面积
// 计算创建正方形对象,可以给构造器传递宽和高
// 也可以给构造器传递一个包含了宽和高的形状参数对象,这样需要用构造器重载
type type_ChartParam = { // 各个图形求面积参数
width?: number,
height?: number,
radius?: number
} class Square {
public width: number;
public height: number; constructor(width_: number, height_: number) // 重载签名
constructor(value_: type_ChartParam) // 重载签名
// constructor(value_: number | type_ChartParam) { // 实现签名
constructor(paramObjOrWidth_: any, height_: number = 0) { // 实现签名
if (typeof paramObjOrWidth_ === 'object') {
this.width = paramObjOrWidth_.width;
this.height = paramObjOrWidth_.height;
} else {
this.width = paramObjOrWidth_;
this.height = height_;
} } public getArea(): number {
return this.height * this.width
}
} let square1 = new Square(40, 50);
let chartParamObj: type_ChartParam = {width: 50, height: 90}
let square2 = new Square(chartParamObj);
console.log(square1.getArea()); // => 2000
console.log(square2.getArea()); // => 4500

脚踏实地行,海阔天空飞!

深度掌握TypeScript中的重载【函数重载、方法重载】的更多相关文章

  1. C#构造方法(函数) C#方法重载 C#字段和属性 MUI实现上拉加载和下拉刷新 SVN常用功能介绍(二) SVN常用功能介绍(一) ASP.NET常用内置对象之——Server sql server——子查询 C#接口 字符串的本质 AJAX原生JavaScript写法

    C#构造方法(函数)   一.概括 1.通常创建一个对象的方法如图: 通过  Student tom = new Student(); 创建tom对象,这种创建实例的形式被称为构造方法. 简述:用来初 ...

  2. Java常见面试题02-方法重写和方法重载的区别?方法重载能改变返回值类型吗?

    方法重写和方法重载的区别?方法重载能改变返回值类型吗?       A:Override方法重写和Overload方法重载的区别? Overload是否可以改变返回值类型?可以 方法重写 •  子类中 ...

  3. 《从零开始学Swift》学习笔记(Day 7)——Swift 2.0中的print函数几种重载形式

    原创文章,欢迎转载.转载请注明:关东升的博客 Swift 2.0中的print函数有4种重载形式: l   print(_:).输出变量或常量到控制台,并且换行. l   print(_:_:).输出 ...

  4. Java中的(构造方法、方法重载、final修饰符使用及继承和抽象)

    构造方法: 构造方法的名称和类名相同,没有返回类型,参数列表(类型.个数)不同 方法重载:成员方法和构造方法都可以进行重载 方法名相同但是参数列表(类型,个数)不同,成为方法的重载. 继承:直支持单继 ...

  5. String类中的一些函数使用方法

    最常用的就是Length()函数了,求字符串的长度 String s="";int i=s.length();i结果为0. 如果是String s=null;int i=s.len ...

  6. Thinkphp模板中使用自定义函数的方法

    注意:自定义函数要放在项目应用目录/common/common.php中. 这里是关键. 模板变量的函数调用格式:{$varname|function1|function2=arg1,arg2,### ...

  7. 在artTemplate的标签中使用外部函数的方法

    第一步,声明函数,并将函数作为data对象的属性.例如: var resArray = new Array(); function beforeRender(data) { //addToArray为 ...

  8. VC中常见API函数使用方法(经验版)

    ***********************************************声明*************************************************** ...

  9. php中调用用户自定义函数的方法:call_user_func,call_user_func_array

    看UCenter的时候有一个函数call_user_func,百思不得其解,因为我以为是自己定义的函数,结果到处都找不到,后来百度了一下才知道call_user_func是内置函数,该函数允许用户调用 ...

  10. 关于QString中的arg()函数使用方法

    例:正确做法:ui->label->setText(QString("Processingfile%1").arg(index));错误做法: ui->label ...

随机推荐

  1. docker swarm 使用详解

    转载请注明出处: 1.docker swarm 的组成架构 一个基本的docker swarm 的架构如下: 它主要包含这几个核心组件: Manager节点(Manager Nodes): 管理节点是 ...

  2. vite 找不到依赖模块:[plugin:vite:dep-pre-bundle]

    问题描述: 运行项目时,出现[plugin:vite:dep-pre-bundle] 错误.这种问题一般为依赖的包未正常配置相关字段,导致vite无法找到包的入口. 遇到这种模块内.找不到引用模块的, ...

  3. [PWN之路]栈溢出那些事儿

    前言 如果入门,想要学习栈溢出相关知识欢迎参考hash_hash的入门文章和我的集训wp,按照buuctf的题目一点一点做,不会的搜索到网上,并且及时在论坛发帖总结和交流.并且这里贴上一个不错的教程, ...

  4. Git-入门使用资料

    一.Git入门教程 Git入门视频,针对于小白快速入门,时常约2~3小时 Git入门视频 相关课件资料: https://pan.baidu.com/s/1U-s4OmkToXJ5Y7BbJ7w2Ww ...

  5. 【Bash】rm -r 与 rmdir 区别

    目录 背景 二者区别 rmdir rm -r rm -rf 测试过程 配置环境 rmdir rm -r rm -rf 参考资料 背景 今天学弟在使用 NVMe-over-TCP 时发现无法卸载 nvm ...

  6. 【测试】自定义配置 RocksDB 进行 YCSB 测试

    目录 简介 编译 RocksDB 编译 YCSB 修复报错 自定义配置 RocksDB 进行 YCSB 测试 参考资料 本文主要记录在利用 YCSB 使用配置文件测试 RocksDB 的过程中遇到的一 ...

  7. 优化Redis缓存淘汰机制解决性能测试中报错率逐渐攀升问题

    在某个查询场景的性能测试过程中,遇到了一个问题:测试过程中报错率逐渐攀升.进一步检查后发现,在查询业务所在应用的后台日志和平台应用的后台日志中,都出现了用户登录相关的报错信息.经过排查分析,发现了问题 ...

  8. Jitpack发布Android库带文档和源码

    原文地址: Jitpack发布Android库带文档和源码 - Stars-One的杂货小窝 忽然发现自己发布的xAndroidUtil库 写代码的时候看方法注释都看不到,研究了下如何让Jitpack ...

  9. 文心一言 VS 讯飞星火 VS chatgpt (96)-- 算法导论9.3 1题

    一.用go语言,在算法 SELECT 中,输人元素被分为每组 5 个元素.如果它们被分为每组 7个元素,该算法仍然会是线性时间吗?证明:如果分成每组 3 个元素,SELECT 的运行时间不是线性的. ...

  10. Java 21新特性:Sequenced Collections(有序集合)

    在JDK 21中,Sequenced Collections的引入带来了新的接口和方法来简化集合处理.此增强功能旨在解决访问Java中各种集合类型的第一个和最后一个元素需要非统一且麻烦处理场景. 下面 ...