在Javascript中伪造函数重载的最好方法是什么?

我知道在Javascript中不可能像在其他语言中那样重载函数。 如果我需要一个函数有两个使用foo(x)和foo(x,y,z),这是最好的/首选的方式:

一开始就用不同的名字 使用可选参数,如y = y || 'default' 使用参数数量 检查参数类型 还是如何?


当前回答

我不确定最佳实践,但我是这样做的:

/*
 * Object Constructor
 */
var foo = function(x) {
    this.x = x;
};

/*
 * Object Protoype
 */
foo.prototype = {
    /*
     * f is the name that is going to be used to call the various overloaded versions
     */
    f: function() {

        /*
         * Save 'this' in order to use it inside the overloaded functions
         * because there 'this' has a different meaning.
         */   
        var that = this;  

        /* 
         * Define three overloaded functions
         */
        var f1 = function(arg1) {
            console.log("f1 called with " + arg1);
            return arg1 + that.x;
        }

        var f2 = function(arg1, arg2) {
             console.log("f2 called with " + arg1 + " and " + arg2);
             return arg1 + arg2 + that.x;
        }

        var f3 = function(arg1) {
             console.log("f3 called with [" + arg1[0] + ", " + arg1[1] + "]");
             return arg1[0] + arg1[1];
        }

        /*
         * Use the arguments array-like object to decide which function to execute when calling f(...)
         */
        if (arguments.length === 1 && !Array.isArray(arguments[0])) {
            return f1(arguments[0]);
        } else if (arguments.length === 2) {
            return f2(arguments[0], arguments[1]);
        } else if (arguments.length === 1 && Array.isArray(arguments[0])) {
            return f3(arguments[0]);
        }
    } 
}

/* 
 * Instantiate an object
 */
var obj = new foo("z");

/*
 * Call the overloaded functions using f(...)
 */
console.log(obj.f("x"));         // executes f1, returns "xz"
console.log(obj.f("x", "y"));    // executes f2, returns "xyz"
console.log(obj.f(["x", "y"]));  // executes f3, returns "xy"

其他回答

介绍

到目前为止,阅读这么多答案会让任何人感到头疼。任何试图了解这个概念的人都需要知道以下先决条件。

函数重载定义,函数长度属性,函数参数属性

最简单形式的函数重载意味着函数根据传递给它的参数数量执行不同的任务。值得注意的是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);

在JS中没有实际的重载,无论如何我们仍然可以用几种方式模拟方法重载:

方法# 1: 使用对象

function test(x,options){
  if("a" in options)doSomething();
  else if("b" in options)doSomethingElse();
}
test("ok",{a:1});
test("ok",{b:"string"});

方法# 2: 使用rest (spread)参数

function test(x,...p){
 if(p[2])console.log("3 params passed"); //or if(typeof p[2]=="string")
else if (p[1])console.log("2 params passed");
else console.log("1 param passed");
}

方法# 3: 使用未定义的

function test(x, y, z){
 if(typeof(z)=="undefined")doSomething();
}

方法# 4: 类型检查

function test(x){
 if(typeof(x)=="string")console.log("a string passed")
 else ...
}

用形参重载函数的最好方法不是检查实参长度或类型;检查类型只会让你的代码变慢,你可以享受数组、空值、对象等的乐趣。

大多数开发人员所做的是将一个对象作为方法的最后一个参数。这个物体可以装任何东西。

function foo(a, b, opts) {
  // ...
  if (opts['test']) { } //if test param exists, do something.. 
}


foo(1, 2, {"method":"add"});
foo(3, 4, {"test":"equals", "bar":"tree"});

然后你可以在你的方法中任意处理它。[开关,if-else,等等]

有两种方法可以更好地解决这个问题:

如果您希望保留很大的灵活性,则传递一个字典(关联数组) 将一个对象作为参数,并使用基于原型的继承来增加灵活性。

Typescript手册提到了重载。虽然前面已经提到过检查类型和根据结果执行不同的逻辑,但这种定义多个命名函数来使用类型系统的方法可能会让读者感兴趣。下面是TypeScript如何实现创建一个接受多种类型参数的函数,这些参数将指导函数逻辑根据传入的参数和类型做不同的事情:

答案是为同一个函数提供多个函数类型作为重载列表。这个列表是编译器用来解析函数调用的。让我们创建一个重载列表,用来描述pickCard接收和返回的内容。

let suits = ["hearts", "spades", "clubs", "diamonds"];

function pickCard(x: { suit: string; card: number }[]): number;
function pickCard(x: number): { suit: string; card: number };
function pickCard(x: any): any {
  // Check to see if we're working with an object/array
  // if so, they gave us the deck and we'll pick the card
  if (typeof x == "object") {
    let pickedCard = Math.floor(Math.random() * x.length);
    return pickedCard;
  }
  // Otherwise just let them pick the card
  else if (typeof x == "number") {
    let pickedSuit = Math.floor(x / 13);
    return { suit: suits[pickedSuit], card: x % 13 };
  }
}

let myDeck = [
  { suit: "diamonds", card: 2 },
  { suit: "spades", card: 10 },
  { suit: "hearts", card: 4 },
];

let pickedCard1 = myDeck[pickCard(myDeck)];
alert("card: " + pickedCard1.card + " of " + pickedCard1.suit);

let pickedCard2 = pickCard(15);
alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);

有了这个更改,重载现在为我们提供了对pickCard函数的类型检查调用。

为了让编译器选择正确的类型检查,它遵循与底层JavaScript类似的过程。它查看重载列表,然后继续执行第一个重载,尝试使用提供的参数调用函数。如果找到匹配项,则选择此重载作为正确的重载。出于这个原因,通常按照最特定到最不特定的顺序对重载进行排序。

注意,函数pickCard(x):任何块都不是重载列表的一部分,因此它只有两个重载:一个接受对象,另一个接受数字。用任何其他参数类型调用pickCard都会导致错误。