分享下《JavaScript忍者秘籍》中的一种函数重载方式
首先说一下函数的length属性:
所有的函数都有一个经常被忽略的属性,但却可以让我们了解函数的声明,那就是length属性。和arguments参数的length属性不同的是,它表示函数声明时所需传入的形参数量。
function makeNinja(name){};
//makeNinja.length === 1;复制代码
对一个函数,在参数方面,我们可以确定两件事情:
- 通过其length属性,可以知道声明了多少命名参数
- 通过arguments.length,可以知道在调用时传入了多少参数
重载函数的方法:
/*
* object为要绑定方法的对象
* name为绑定方法所用的属性名称
* fn为要绑定的方法
* */
function addMethod(object, name, fn) {
//保存原有的函数,因为调用的时候可能不匹配传入的参数个数
var old = object[name];
//创建一个新匿名函数作为新方法
object[name] = function () {
//如果该匿名函数的形参个数和实参个数匹配,就调用该函数
if (fn.length === arguments.length) {
return fn.apply(this, arguments)
//如果传入的参数不匹配,则调用原有的参数
} else if (typeof old === 'function') {
return old.apply(this, arguments);
}
}
}复制代码
用法
var ninja = {};
addMethod(ninja, 'whatever', function () {
/* do something */
});
addMethod(ninja, 'whatever', function (a) {
/* do something else */
});
addMethod(ninja, 'whatever', function (a, b) {
/* yet something else */
});复制代码
分析
addMethod()第一次调用将创建一个新匿名函数,传入零个参数进行调用的时候将会调用该fn函数。由于此时ninja是一个新对象,所以这时候不用担心之前创建的方法。
addMethod()第二次调用的时候,首先将之前的同名函数保存到一个变量old中,然后将新创建的匿名函数作为方法。新方法首先检查传入的函数个数是否为一,如果是,就调用刚才传入的fn函数;如果不是,则重新调用存储在old上的函数,重新调用该函数时,将会再次检查参数个数是否为零,继而调用参数个数为零的fn版本的函数。
addMethod()第三次调用的时候,传入了一个接收两个参数的fn函数,然后判断逻辑相同:创建一个匿名函数作为方法,判断是否传入参数的个数为两个,如果是则调用两个参数的fn函数,否则继续判断参数个数是否为一。
这种方法就像剥洋葱一样,每一层都检查参数个数是否匹配,如果不匹配的话,就进入上一层创建的函数。内部函数是通过闭包访问到old和fn的。
这是一个绝佳的技巧,因为这些函数实际上并没有存储于任何典型的数据结构中,而是在闭包里作为引用进行存储。应该注意的是,在使用这个特定的技巧时,需要注意以下两点:
- 这个重载只适用于不同数量的参数,但并不区分类型、参数名或其他东西。这些才是我们经常想做的事情
- 这个重载方法会有一些函数调用的开销