前几天在社区看到一篇精华贴:“实现js重载”。
看到这个标题,我首先想到的是使用函数的arguments.length做case判断,从而调用不同的回调,实现重载。
果不其然,在实现中大多数人也都是这么操作的。
然而当我阅读完整篇帖子后,才发现竟然还有更巧妙的方法,这是我万万没想到的,于是我记录了下来。



一、介绍一下重载的概念和举例

重载,就是函数或者方法有相同的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者重载方法。

简单翻译为:函数名相同,参数个数不同,执行的代码不同。

举个栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
* 重载
*/
function fn(name) {
console.log(`我是${name}`);
}

function fn(name, age) {
console.log(`我是${name},今年${age}岁`);
}

function fn(name, age, sport) {
console.log(`我是${name},今年${age}岁,喜欢运动是${sport}`);
}

/*
* 调用fn函数的理想结果
*/
fn('张三') // 我是张三
fn('张三', 18) // 我是张三,今年18岁
fn('张三', 18, '打篮球') // 我是张三,今年18岁,喜欢运动是打篮球

上面这个栗子在js中直接这么写肯定是不行的,

对于同名的函数声明,Javascript采用的是覆盖原则,先声明的会被覆盖,因为函数在声明时会指定函数的内容,所以同一作用域下一系列同名函数声明的最终结果是调用时函数的内容和最后一次函数声明相同。

因此上面的代码执行起来会是这样的效果:

1
2
3
我是张三,今年undefined岁,喜欢运动是undefined
我是张三,今年18岁,喜欢运动是undefined
我是张三,今年18岁,喜欢运动是打篮球

二、利用arguments.length做case判断实现

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

function fn() {
switch (arguments.length) {
case 1:
var [name] = arguments
console.log(`我是${name}`)
break;
case 2:
var [name, age] = arguments
console.log(`我是${name},今年${age}岁`)
break;
case 3:
var [name, age, sport] = arguments
console.log(`我是${name},今年${age}岁,喜欢运动是${sport}`)
break;
}
}

/*
* 实现效果
*/
fn('张三') // 我是张三
fn('张三', 18) // 我是张三,今年18岁
fn('张三', 18, '打篮球') // 我是张三,今年18岁,喜欢运动是打篮球


相信大多数同志也是用这种方式实现重载的,毫不客气的说 我在部分项目里也能看到这种判定方式。

但是今天不一样了,我需要告诉大家的是,还有更骚的方式,当然也就是大家能想到但却不常使用的方式:利用闭包。

三、利用闭包实现

先直接贴代码:

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
function addMethod(object, name, fn) {
var old = object[name]; //把前一次添加的方法存在一个临时变量old里面
object[name] = function () { // 重写object[name]的方法
// 如果调用object[name]方法时,传入的参数个数跟预期的一致,则直接调用
if (fn.length === arguments.length) {
return fn.apply(this, arguments);
// 否则,判断old是否是函数,如果是,就调用old
} else if (typeof old === "function") {
return old.apply(this, arguments);
}
}
}

addMethod(window, 'fn', (name) => console.log(`我是${name}`))
addMethod(window, 'fn', (name, age) => console.log(`我是${name},今年${age}岁`))
addMethod(window, 'fn', (name, age, sport) => console.log(`我是${name},今年${age}岁,喜欢运动是${sport}`))

/*
* 实现效果
*/

window.fn('张三') // 我是张三
window.fn('张三', 18) // 我是张三,今年18岁
window.fn('张三', 18, '打篮球') // 我是张三,今年18岁,喜欢运动是打篮球


贴上原贴地址:浅谈JavaScript函数重载

最后,重载的使用场景还可以在设计一些函数库或者处理同函数名的多个函数参数时考虑使用。
虽然对于追求函数式开发的理念来说可能有些违背,但作为学习资料扩充自己的知识边界也是值得推敲的。