js函数重载知识
✨什么是函数重载?
解释:即同名函数根据参数类型/数量自动选择实现,JavaScript 本身不支持传统意义上的函数重载,但可以通过以下方法模拟类似功能:
直接上代码
function addMethods(obj, name, fn) {
const old = obj[name];
obj[name] = function (...args) {
if (args.length === fn.length) {
return fn.apply(this, args);
} else if (typeof old === "function") {
return old.apply(this, args);
}
};
}
const testObj = {};
addMethods(testObj, "testFn", () => {
console.log("没有参数");
});
addMethods(testObj, "testFn", (a) => {
console.log("有一个参数", a);
});
addMethods(testObj, "testFn", (a, b) => {
console.log("有2个参数", a, b);
});
这里的流程是:
1.定义一个空对象`testObj`为`{}`;
2.定义一个为`testObj`添加函数的方法`addMethods`函数,参数分别为1个初始`object`对象、`函数名称`、`执行函数`;
3.执行`addMethods`函数以及你想添加的函数重载执行方法;
现在可以在控制台执行testObj.testFn函数传入不同参数执行不同的逻辑
看一看效果:

「🧠分析」
`addMethods`设计了`old`一个变量名接受最新的函数,并且在后面根据调用的函数参数不同形成链式查找到对应函数执行
优点✅:简单几行代码实现js重载
缺点❌:不能根据参数类别执行对应方法,只能根据不同数量执行
⭐进阶方法
假如我们想根据同名函数、形参数量也相同,只是形参的类型可能不同而分别执行不同的逻辑,像这样:
testFn(1,2,3)//打印number number number testFn(1,'2','3')//打印number string string testFn(1,2,3,false)//打印number number number boolean ...
一步一步分析
1️⃣因为最终我们调用是以testFn(1,2,3)这种形式,那么testFn逻辑怎么确定呢?首先我们肯定能拿到参数的数量和类型,然后我们可以根据这个数量和类别组合生成1个唯一的`key`值来对执行对应的函数,既然需要有`key=>value`的形式那么自然而然想到map结构来保存`key`以及对应的`function`
export const createOverload = () => {
const map=new Map()
const testFn=(...args:any[])=>{
}
const addMethods=(...args:any[])=>{
}
};
2️⃣想要用map保存`function`,首先我们肯定得添加,创建一个addMethods方法用于添加函数,核心逻辑就是提取其中的形参和需要执行的函数形成`key=>function`的映射
export const createOverload = () => {
const map = new Map();
const addMethods = (...args: any[]) => {};
const testFn= (...args:any[]) => {
const mapKey = args.map((item) => typeof item).join(",");
const fn = map.get(mapKey);
if (!fn) {
console.log("没有找到执行函数");
} else {
fn.apply(this, args);
}
};
};
3️⃣testFn的逻辑自然而然就是根据执行时传递的参数找到对应`function`并且执行了
export const createOverload = () => {
const map = new Map();
const addMethods = (...args: any[]) => {
const fn = args.pop();
if (typeof fn !== "function") {
console.log("没有传递执行函数");
} else {
map.set(args.join(","), fn);
}
};
const testFn = (...args: any[]) => {
const mapKey = args.map((item) => typeof item).join(",");
const fn = map.get(mapKey);
if (!fn) {
console.log("没有找到执行函数");
} else {
fn.apply(this, args);
}
};
testFn.addMethods = addMethods;
return testFn;
};
测试一下
import { createOverload } from "@/utils/index";
const a = createOverload();
a.addMethods("number", "string", (a, b) => {
console.log("number string", a, b);
});
a.addMethods("string", "string", (a, b) => {
console.log("string string", a, b);
});
a.addMethods("number", "number", (a, b) => {
console.log("number number", a, b);
});
a.addMethods("string", "number", "number", (a, b, c) => {
console.log("number number number", a, b, c);
});
a.addMethods("boolean", "boolean", "number", (a, b, c) => {
console.log("boolean boolean number", a, b, c);
});
a(1, 2, 3);
a("1", "2");
a(1, 2);
a("1", 2, 3);
a(true, false, 1);

❤️至此就弥补了第一种方法的不足之处
⚔️闲谈之余
假如我们要给addMethods这个函数添加ts类别怎么添加呢?
1️⃣首先`addMethods`函数最后一个参数肯定是function,前面的参数是不固定的基本数据类型的字符串,定义一个`AllType`然后使用ts中关键字`keyof`索引查询来定义表示具体类型的`typeKey`
type AllType = {
boolean: boolean;
string: string;
number: number;
symbol: symbol;
bigint: bigint;
null: null;
undefined: undefined;
object: object;
function: Function
};
type typeKey = keyof AllType;
declare function AddImp(
...args: [...typeKey[], (...args:?[]) => void]
): void;
2️⃣前面不定量参数的类型确定了,那传入函数的参数类型是什么呢?可以确定的是传入函数的数量和类型肯定是与前面相关联的,像下面这样,即数量和类型是相关联的,那么就要想到使用`ts`中的`泛型`
addMethods("string", "number", "number", (a:string, b:number, c:number) => {
console.log("number number number", a, b, c);
});
addMethods("number", "number", (a:number, b:number) => {
console.log("number number", a, b);
});
...
修改AddImp
type AllType = {
boolean: boolean;
string: string;
number: number;
symbol: symbol;
bigint: bigint;
null: null;
undefined: undefined;
object: object;
function:Function
};
type typeKey = keyof AllType;
declare function AddImp<T extends typeKey[]>(
...args: [...T, (...args: ?[]) => void]
): void;
3️⃣具体如何关联起来呢?
**举个栗子:**
假如我前面的参数传了3个分别是**字符串**`string`、`number`、`number`,那么我后面函数参数a,b,c所传的实参**类型**肯定就是要分别对应是`string`、`number`、`number`,既然前面的**字符串参数**为约定为了**泛型**`T`,那么我就需要找到这个`T`所对应的原始类型
addMethods("string", "number", "number", (a:string, b:number, c:number) => {
console.log("string number number", a, b, c);
});
**定义一个高级类型`transFormKey`,专门用于获取根据其传入的字符串参数获取其对应`AllType`中的原始类型**
**类型代码**
type AllType = {
boolean: boolean;
string: string;
number: number;
symbol: symbol;
bigint: bigint;
null: null;
undefined: undefined;
object: object;
function:Function
};
type typeKey = keyof AllType;
type transFormKey<T extends typeKey[]> = {
[P in keyof T]: AllType[T[P]];
};
declare function AddImp<T extends typeKey[]>(
...args: [...T, (...args: transFormKey<T>) => void]
): void;
将类型添加到之前的函数上再试试
export const createOverload = () => {
const map = new Map();
const addMethods: typeof AddImp = (...args) => {
const fn = args.pop();
if (typeof fn !== "function") {
console.log("没有传递执行函数");
} else {
map.set(args.join(","), fn);
}
};
const testFn = (...args: any[]) => {
const mapKey = args.map((item) => typeof item).join(",");
const fn = map.get(mapKey);
if (!fn) {
console.log("没有找到执行函数");
} else {
fn.apply(this, args);
}
};
testFn.addMethods = addMethods;
return testFn;
};
添加完之后,在调用的时候就能看到类型提示了

并且如果函数的参数数目和前面的不一致时就会报类型错误

完结🎉

浙公网安备 33010602011771号