JS 实现函数重载

引言

众所周知, JS 默认是不支持函数重载的, 如果强制这么写会出现下面这种情况:

1
2
3
4
5
6
7
8
9
10
const search = (name) => {
console.log("按照名字查找");
};

const search = (name, age) => {
console.log("按照名字和年龄查找");
};

search("Kaede");
search("YYT", 18);
1
2
3
const search = (name, age) => {
^
SyntaxError: Identifier 'search' has already been declared

所以应该怎么实现函数的重载呢? 这里有两种方案.

方案 1

我们可以创建一个类, 然后给这个类添加多个同名函数, 进而实现重载. 这是 JQuery 中的做法. 不过这里的添加函数的方法需要单独实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//第一个参数是要绑定函数的对象,第二个函数是函数的名字,第三个是具体函数
function addMethod(Object, name, fn) {
// 具体实现
// 因为方法都是重名的,所以先取出原本的方法
const old = Object[name]; // ...args是形参表示接受所有参数
Object[name] = function (...args) {
// 判断形参个数,如果和传入换上的形参个数一样就执行传入的这个函数
if (args.length === fn.length) {
return fn.apply(this, args);
} else if (typeof old === "function") {
return old.apply(this, args);
}
};
}

// 随后新建一个对象 用来实现函数重载
const tool = {};

// 添加方法
addMethod(tool, "search", () => {
console.log("直接查找用户");
});

addMethod(tool, "search", (name) => {
console.log("通过姓名查找用户");
});

addMethod(tool, "search", (name, age) => {
console.log("通过姓名和年龄查找用户");
});

tool.search();
tool.search("Kaede");
tool.search("Yyt", 18);

则输出正常. 重载成功.

1
2
3
直接查找用户
通过姓名查找用户
通过姓名和年龄查找用户

问题就是不方便, 它是一个对象; 另外这里只能判断参数的数量, 不能判断参数的类型, 不是很好.

方案 2

通过一个映射来实现即可. 还是写一个工具函数出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
const createOverload = () => {
// 定义map 形成映射
const fnMap = new Map();

// 定义调用的方法
const overload = (...args) => {
// 根据目标函数 创建一个映射关系
const key = args.map((item) => typeof item).join(".");

// 随后 尝试从映射中获取这个key
const fn = fnMap.get(key);

// 如果不存在 则报错, 函数不存在
if (!fn) {
throw new TypeError("没有找到对应的实现");
}

// 否则就是找到了 进行函数的调用操作
return fn.apply(this, args);
};

// 定义重载方法
overload.addImpl = (...args) => {
// 获取需要重载的函数
const fn = args.pop();
// 判断最后一个参数是不是函数
if (typeof fn !== "function") {
throw new TypeError("最后一个参数应当为函数");
}
// 否则可以重载 记录key
const key = args.join(".");
fnMap.set(key, fn);
};

// 返回overload
return overload;
};

随后, 按照如下方式进行调用即可.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { createOverload } from "./util.js";
const getData = createOverload();

// 重载
getData.addImpl(() => {
console.log("空参的函数");
});

getData.addImpl("string", (name) => {
console.log("根据姓名查找 姓名为" + name);
});

getData.addImpl("string", "number", (name, age) => {
console.log("根据姓名和年龄查找 分别为:" + name + ", " + age);
});

// 调用
getData();
getData("Kaede");
getData("YYT", 18);
getData("YYT", "LC");

输出如下, 可以看到能够根据类型正确的重载函数了.

1
2
3
4
5
6
7
8
空参的函数
根据姓名查找 姓名为Kaede
根据姓名和年龄查找 分别为:YYT, 18
file:///D:/Documents/WorkSpace/test-projects/js-temp/util.js:15
throw new TypeError("没有找到对应的实现");
^

TypeError: 没有找到对应的实现