javaScript 中如何模拟实现方法的重载,动手实现下
一、背景知识
JavaScript 不支持重载的语法,它没有重载所需要的的函数签名。ECMAScript 函数不能向传统意义上那样实现重载。而在其他语言(如Java)中,可以为一个函数编写多个定义,只要这两个定义的签名(接受的参数的类型和数量)不同即可。如前所述,ECMAScript 函数没有签名,因为其参数是由包含零活多个值得数组来表示的。而没有函数签名,真的的重载是不可能做到的。
二、什么是函数重载
重载函数时函数的一种特殊情况,为方便使用,允许在同一范围中声明几个功能类似的同名函数,但是这些同名函数的形式参数(值参数的个数、类型或者顺序)必须不同,也就是说同一个函数完成不同的功能。这就是重载函数。
三、模拟实现
利用闭包特性
addMethod 函数接受 3 个参数:目标对象、目标方法名、函数体,当函数被调用时:
1.先将目标 object[name] 的值存入变量 old 中,因此起初 old 中的值可能不是一个函数;
2.接着向 object[name] 赋值一个代理函数,并且由于变量 old 、 fnt 在代理函数中被引用,所以 old、fnt 将常驻内存不被回收
代理函数被调用时:
1. 先判断传入参数与其父级作用域中 fn 期望接收参数的个数是否一致,若是则调用该 fn;
2. 若否,则判断其父级作用域中 old 值类型是否为函数,若是则调用该 old;
3. 当 old 中存有上一次生成的代理函数时,则会重复前面两个步骤,直至 old 值不为代理函数。
function addMethod(object, name, fnt) {
// 保存前一个值,以便后续调用
var old = object[name];
object[name] = function () {
// 向object[name]赋值一个代理函数
// 判断fnt期望接收的参数与传入参数个数是否一致
if (fnt.length === arguments.length) {
// 若是,则调用fnt
return fnt.apply(this, arguments)
} else if (typeof old === 'function') {
// 若否,则判断old的值是否为函数
// 若是,则调用old
return old.apply(this, arguments);
}
};
}
//模拟重载add
var methods = {};
//添加方法,顺序无关
addMethod(methods, 'add', function () {
return 0
});
addMethod(methods, 'add', function (a, b) {
return a + b
});
addMethod(methods, 'add', function (a, b, c) {
return a + b + c
});
//执行
console.log(methods.add()); //0
console.log(methods.add(10, 20)); //30
console.log(methods.add(10, 20, 30)); //60
我再再来一个投机取巧易理解的方式:
function addMethod(obj, name, fn) {
//形参数量
let len = fn.length;
//根据形参记录fn
obj._overLoad_ ? obj._overLoad_[len] = fn : obj._overLoad_ = {
[len]: fn
};
//重载名
!obj[name] && (obj[name] = function () {
//实参数量
let tLen = arguments.length;
//赋值方法
return obj._overLoad_[tLen] ? obj._overLoad_[tLen].apply(this, arguments) : undefined;
})
}
//模拟重载add
var methods = {};
//添加方法,顺序无关
addMethod(methods, 'add', function () {
return 0
});
addMethod(methods, 'add', function (a, b, c) {
return a + b + c
});
addMethod(methods, 'add', function (a, b) {
return a + b
});
//执行
console.log(methods.add()); //0
console.log(methods.add(10, 20)); //30
console.log(methods.add(10, 20, 30)); //60