在Javascript中伪造函数重载的最好方法是什么?
我知道在Javascript中不可能像在其他语言中那样重载函数。 如果我需要一个函数有两个使用foo(x)和foo(x,y,z),这是最好的/首选的方式:
一开始就用不同的名字 使用可选参数,如y = y || 'default' 使用参数数量 检查参数类型 还是如何?
在Javascript中伪造函数重载的最好方法是什么?
我知道在Javascript中不可能像在其他语言中那样重载函数。 如果我需要一个函数有两个使用foo(x)和foo(x,y,z),这是最好的/首选的方式:
一开始就用不同的名字 使用可选参数,如y = y || 'default' 使用参数数量 检查参数类型 还是如何?
当前回答
你可以使用John Resig的addMethod。使用此方法,您可以根据参数计数“重载”方法。
// addMethod - By John Resig (MIT Licensed)
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 );
};
}
我还创建了该方法的替代方法,使用缓存来保存函数的变体。这里描述了不同之处
// addMethod - By Stavros Ioannidis
function addMethod(obj, name, fn) {
obj[name] = obj[name] || function() {
// get the cached method with arguments.length arguments
var method = obj[name].cache[arguments.length];
// if method exists call it
if ( !! method)
return method.apply(this, arguments);
else throw new Error("Wrong number of arguments");
};
// initialize obj[name].cache
obj[name].cache = obj[name].cache || {};
// Check if a method with the same number of arguments exists
if ( !! obj[name].cache[fn.length])
throw new Error("Cannot define multiple '" + name +
"' methods with the same number of arguments!");
// cache the method with fn.length arguments
obj[name].cache[fn.length] = function() {
return fn.apply(this, arguments);
};
}
其他回答
多年来,我一直在使用这个函数来美化我的重载:
function overload(){
const fs = arguments, fallback = fs[fs.length - 1];
return function(){
const f = fs[arguments.length] || (arguments.length >= fs.length ? fallback : null);
return f.apply(this, arguments);
}
}
型:
function curry1(f){
return curry2(f, f.length);
}
function curry2(f, minimum){
return function(...applied){
if (applied.length >= minimum) {
return f.apply(this, applied);
} else {
return curry2(function(...args){
return f.apply(this, applied.concat(args));
}, minimum - applied.length);
}
}
}
export const curry = overload(null, curry1, curry2);
看看jQuery的off方法:
function off( types, selector, fn ) {
var handleObj, type;
if ( types && types.preventDefault && types.handleObj ) {
// ( event ) dispatched jQuery.Event
handleObj = types.handleObj;
jQuery( types.delegateTarget ).off(
handleObj.namespace ?
handleObj.origType + "." + handleObj.namespace :
handleObj.origType,
handleObj.selector,
handleObj.handler
);
return this;
}
if ( typeof types === "object" ) {
// ( types-object [, selector] )
for ( type in types ) {
this.off( type, selector, types[ type ] );
}
return this;
}
if ( selector === false || typeof selector === "function" ) {
// ( types [, fn] )
fn = selector;
selector = undefined;
}
if ( fn === false ) {
fn = returnFalse;
}
return this.each( function() {
jQuery.event.remove( this, types, fn, selector );
} );
}
许多重载函数在优化性能时几乎不可读。你必须破译函数的头部。这可能比使用我提供的重载函数更快;但是,从人的角度来看,它在识别调用了哪个重载方面要慢一些。
这是一个老问题,但我认为需要另一个条目(尽管我怀疑是否有人会读它)。立即调用函数表达式(IIFE)的使用可以与闭包和内联函数结合使用,以允许函数重载。考虑以下(人为的)例子:
var foo;
// original 'foo' definition
foo = function(a) {
console.log("a: " + a);
}
// define 'foo' to accept two arguments
foo = (function() {
// store a reference to the previous definition of 'foo'
var old = foo;
// use inline function so that you can refer to it internally
return function newFoo(a,b) {
// check that the arguments.length == the number of arguments
// defined for 'newFoo'
if (arguments.length == newFoo.length) {
console.log("a: " + a);
console.log("b: " + b);
// else if 'old' is a function, apply it to the arguments
} else if (({}).toString.call(old) === '[object Function]') {
old.apply(null, arguments);
}
}
})();
foo(1);
> a: 1
foo(1,2);
> a: 1
> b: 2
foo(1,2,3)
> a: 1
简而言之,IIFE的使用创建了一个局部作用域,允许我们定义私有变量old来存储对函数foo的初始定义的引用。这个函数然后返回一个内联函数newFoo,如果传递给它的恰好是两个参数a和b,或者调用旧函数if参数,则该函数将记录两个参数的内容。长度!== 2。此模式可以重复任意次数,以赋予一个变量若干不同的函数定义。
我正在做一个库,提供类的代码功能,Javascript,目前它支持构造函数,继承,方法重载参数的数量和参数的类型,混合,静态属性和单例。
请参阅使用该库的方法重载示例:
eutsiv.define('My.Class', {
constructor: function() {
this.y = 2;
},
x: 3,
sum: function() {
return this.x + this.y;
},
overloads: {
value: [
function() { return this.x + ', ' + this.y },
function(p1) { this.x = p1; },
function(p1, p2) { this.x = p1; this.y = p2; } // will set x and y
]
}
});
var test = new My.Class({ x: 5 }); // create the object
test.value(); // will return '5, 2'
test.sum(); // will return 7
test.value(13); // will set x to 13
test.value(); // will return '13, 2'
test.sum(); // will return 15
test.value(10, 20); // will set x to 10 and y to 20
test.value(); // will return '10, 20'
test.sum(); // will return 30
欢迎任何反馈,bug修复,文档和测试改进!
https://github.com/eutsiv/eutsiv.js
介绍
到目前为止,阅读这么多答案会让任何人感到头疼。任何试图了解这个概念的人都需要知道以下先决条件。
函数重载定义,函数长度属性,函数参数属性
最简单形式的函数重载意味着函数根据传递给它的参数数量执行不同的任务。值得注意的是TASK1, TASK2和TASK3在下面突出显示,它们是根据传递给同一个函数fooYo的参数数量来执行的。
// if we have a function defined below
function fooYo(){
// do something here
}
// on invoking fooYo with different number of arguments it should be capable to do different things
fooYo(); // does TASK1
fooYo('sagar'); // does TASK2
fooYo('sagar','munjal'); // does TAKS3
请注意 JS不提供内置的函数重载功能。
替代
John E Resig (JS的创建者)指出了一种替代方法,它使用上述先决条件来实现函数重载的能力。
下面的代码通过使用if-else或switch语句使用了一种简单但简单的方法。
计算参数长度属性。 不同的值导致调用不同的函数。
Var ninja = { function() { 切换(arguments.length) { 例0: /*做某事*/ 打破; 案例1: /* do something else */ 打破; 案例2: /*做其他事情*/ 打破; //等等… } } }
另一种技术更加简洁和动态。这项技术的亮点是addMethod泛型函数。
我们定义了一个addMethod函数,它用于向一个对象添加不同的函数,这些函数具有相同的名称但不同的功能。 下面的addMethod函数接受三个参数:对象名称object,函数名称name和我们想要调用的函数fn。 在addMethod定义中,var old存储了对前一个函数的引用,该函数通过闭包(一个保护气泡)的帮助存储。
函数addMethod(对象,名称,fn) { Var old =对象[name]; 对象[name] = function(){ 如果(fn)。Length == arguments.length) 返回fn。应用(这个参数) Else if (typeof old == 'function') 返回旧。应用(这个参数); }; };
使用调试器来理解代码流。 下面的addMethod添加了三个函数,当使用ninja.whatever(x)调用时,参数x的数量可以是任何东西,即空白或一个或多个调用不同的函数定义,同时使用addMethod函数。
Var ninja = {}; 调试器; addMethod(ninja,'whatever',function(){console.log("我是那个提供了零参数的人")}); addMethod(ninja,'whatever',function(a){console.log("我是提供了一个参数的人")}); addMethod(ninja,'whatever',function(a,b){console.log("我是一个提供了两个参数的人")}); ninja.whatever (); ninja.whatever(1、2); ninja.whatever (3);
JavaScript中没有真正的函数重载,因为它允许传递任意数量的任意类型的参数。你必须检查函数内部传递了多少参数以及它们的类型。