Javascript中的函数重载 - 最佳实践

时间:2009-01-19 00:06:27

标签: javascript overloading

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

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

  1. 首先使用不同的名称
  2. 使用y = y || 'default'
  3. 等可选参数
  4. 使用参数数量
  5. 检查参数类型
  6. 或者怎么样?

38 个答案:

答案 0 :(得分:542)

使用参数执行函数重载的最佳方法是不检查参数长度或类型;检查类型只会让你的代码变慢,你可以享受Arrays,nulls,Objects等的乐趣。

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

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等]

答案 1 :(得分:153)

我经常这样做:

C#:

public string CatStrings(string p1)                  {return p1;}
public string CatStrings(string p1, int p2)          {return p1+p2.ToString();}
public string CatStrings(string p1, int p2, bool p3) {return p1+p2.ToString()+p3.ToString();}

CatStrings("one");        // result = one
CatStrings("one",2);      // result = one2
CatStrings("one",2,true); // result = one2true

JavaScript等效:

function CatStrings(p1, p2, p3)
{
  var s = p1;
  if(typeof p2 !== "undefined") {s += p2;}
  if(typeof p3 !== "undefined") {s += p3;}
  return s;
};

CatStrings("one");        // result = one
CatStrings("one",2);      // result = one2
CatStrings("one",2,true); // result = one2true

这个特殊的例子在javascript中实际上比C#更优雅。未指定的参数在javascript中为'undefined',在if语句中计算结果为false。但是,函数定义不传达p2和p3是可选的信息。如果你需要大量的重载,jQuery决定使用一个对象作为参数,例如,jQuery.ajax(options)。我同意他们这是最强大且可清晰记录的重载方法,但我很少需要一个或两个以上的快速可选参数。

编辑:根据伊恩的建议更改了IF测试

答案 2 :(得分:65)

JavaScript中没有真正的函数重载,因为它允许传递任何类型的任意数量的参数。您必须在函数内部检查已传递了多少arguments以及它们的类型。

答案 3 :(得分:47)

正确的答案是JAVASCRIPT中没有超载。

检查/切换功能内部不是OVERLOADING。

重载的概念: 在某些编程语言中,函数重载或方法重载是能够使用不同的实现创建多个同名方法。对重载函数的调用将运行适合于调用上下文的该函数的特定实现,允许一个函数调用根据上下文执行不同的任务。

例如,doTask()和doTask(对象O)是重载方法。要调用后者,必须将对象作为参数传递,而前者不需要参数,并使用空参数字段调用。一个常见的错误是在第二个方法中为对象分配一个默认值,这会导致一个模糊的调用错误,因为编译器不知道要使用哪两种方法。

https://en.wikipedia.org/wiki/Function_overloading

所有建议的实现都很棒,但事实是,没有JavaScript的原生实现。

答案 4 :(得分:26)

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

  1. 如果您想要保留很大的灵活性,请传递字典(关联数组)

  2. 以对象作为参数并使用基于原型的继承来增加灵活性。

答案 5 :(得分:17)

这是一种允许使用参数类型进行实际方法重载的方法,如下所示:

Func(new Point());
Func(new Dimension());
Func(new Dimension(), new Point());
Func(0, 0, 0, 0);

编辑(2018):由于这是2011年编写的,直接方法调用的速度大大提高,而重载方法的速度却没有。

这不是我推荐的方法,但考虑如何解决这些类型的问题是值得思考的。


以下是不同方法的基准 - https://jsperf.com/function-overloading。它表明,从 16.0(测试版)开始,在 Chrome的V8 中,功能重载(将类型考虑在内)<<> 13倍。< / p>

除了传递一个对象(即{x: 0, y: 0})之外,还可以在适当的时候采用C方法,相应地命名方法。例如,Vector.AddVector(vector),Vector.AddIntegers(x,y,z,...)和Vector.AddArray(integerArray)。您可以查看C库,例如OpenGL,以获得命名灵感。

编辑:我添加了一个基准,用于传递对象并使用'param' in argarg.hasOwnProperty('param')测试对象,并且函数重载比传递一个快得多对象和检查属性(至少在此基准中)。

从设计角度来看,如果重载参数对应于同一个动作,则函数重载仅有效或逻辑。因此,有理由认为应该有一个仅涉及具体细节的基础方法,否则可能表明不适当的设计选择。因此,人们还可以通过将数据转换为相应的对象来解决函数重载的使用问题。当然,必须考虑问题的范围,因为如果你的意图只是打印一个名字,就没有必要进行精心设计,但是对于框架和库的设计,这种想法是合理的。

我的例子来自Rectangle实现 - 因此提到了Dimension和Point。也许Rectangle可以向GetRectangle()Dimension原型添加Point方法,然后对函数重载问题进行排序。原始人怎么样?好吧,我们有参数长度,现在这是一个有效的测试,因为对象有一个GetRectangle()方法。

function Dimension() {}
function Point() {}

var Util = {};

Util.Redirect = function (args, func) {
  'use strict';
  var REDIRECT_ARGUMENT_COUNT = 2;

  if(arguments.length - REDIRECT_ARGUMENT_COUNT !== args.length) {
    return null;
  }

  for(var i = REDIRECT_ARGUMENT_COUNT; i < arguments.length; ++i) {
    var argsIndex = i-REDIRECT_ARGUMENT_COUNT;
    var currentArgument = args[argsIndex];
    var currentType = arguments[i];
    if(typeof(currentType) === 'object') {
      currentType = currentType.constructor;
    }
    if(typeof(currentType) === 'number') {
      currentType = 'number';
    }
    if(typeof(currentType) === 'string' && currentType === '') {
      currentType = 'string';
    }
    if(typeof(currentType) === 'function') {
      if(!(currentArgument instanceof currentType)) {
        return null;
      }
    } else {
      if(typeof(currentArgument) !== currentType) {
        return null;
      }
    } 
  }
  return [func.apply(this, args)];
}

function FuncPoint(point) {}
function FuncDimension(dimension) {}
function FuncDimensionPoint(dimension, point) {}
function FuncXYWidthHeight(x, y, width, height) { }

function Func() {
  Util.Redirect(arguments, FuncPoint, Point);
  Util.Redirect(arguments, FuncDimension, Dimension);
  Util.Redirect(arguments, FuncDimensionPoint, Dimension, Point);
  Util.Redirect(arguments, FuncXYWidthHeight, 0, 0, 0, 0);
}

Func(new Point());
Func(new Dimension());
Func(new Dimension(), new Point());
Func(0, 0, 0, 0);

答案 6 :(得分:16)

最好的方法实际上取决于函数和参数。在不同情况下,您的每个选项都是个好主意。我通常按​​以下顺序尝试这些,直到其中一个有效:

  1. 使用y = y ||等可选参数'default'。如果可以的话,这很方便,但实际上并不总是有用,例如当0 / null / undefined是一个有效的参数时。

  2. 使用参数数量。与上一个选项类似,但在#1不起作用时可能有效。

  3. 检查参数类型。这可以在参数数量相同的某些情况下使用。如果您无法可靠地确定类型,则可能需要使用不同的名称。

  4. 首先使用不同的名称。如果其他选项不起作用,不实用或与其他相关功能保持一致,则可能需要执行此操作。< / p>

答案 7 :(得分:13)

  

如果我需要一个有两个用途的函数foo(x)和foo(x,y,z)这是最好的/首选方式吗?

问题是JavaScript本身不支持方法重载。因此,如果它看到/解析两个或多个具有相同名称的函数,它将只考虑最后定义的函数并覆盖之前的函数。

我认为适合大多数情况的方式之一是 -

让我们说你有方法

function foo(x)
{
} 

您可以定义新方法

,而不是重载无法在javascript 中使用的方法
fooNew(x,y,z)
{
}

然后修改第一个函数,如下所示 -

function foo(arguments)
{
  if(arguments.length==2)
  {
     return fooNew(arguments[0],  arguments[1]);
  }
} 

如果您有许多此类重载方法,请考虑使用switch而不仅仅是if-else语句。

more details

PS:以上链接转到我的个人博客,其中包含其他详细信息。

答案 8 :(得分:9)

我不确定最佳做法,但我的工作方式如下:

/*
 * 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"

答案 9 :(得分:6)

我刚试过这个,也许它适合你的需要。 根据参数的数量,您可以访问其他功能。您在第一次调用时初始化它。 函数映射隐藏在闭包中。

TEST = {};

TEST.multiFn = function(){
    // function map for our overloads
    var fnMap = {};

    fnMap[0] = function(){
        console.log("nothing here");
        return this;    //    support chaining
    }

    fnMap[1] = function(arg1){
        //    CODE here...
        console.log("1 arg: "+arg1);
        return this;
    };

    fnMap[2] = function(arg1, arg2){
        //    CODE here...
        console.log("2 args: "+arg1+", "+arg2);
        return this;
    };

    fnMap[3] = function(arg1,arg2,arg3){
        //    CODE here...
        console.log("3 args: "+arg1+", "+arg2+", "+arg3);
        return this;
    };

    console.log("multiFn is now initialized");

    //    redefine the function using the fnMap in the closure
    this.multiFn = function(){
        fnMap[arguments.length].apply(this, arguments);
        return this;
    };

    //    call the function since this code will only run once
    this.multiFn.apply(this, arguments);

    return this;    
};

测试一下。

TEST.multiFn("0")
    .multiFn()
    .multiFn("0","1","2");

答案 10 :(得分:5)

由于JavaScript没有函数重载选项,因此可以使用对象。如果有一个或两个必需参数,最好将它们与options对象分开。下面是一个示例,说明如果在options对象中未传递value,如何将选项对象和填充值用于默认值。

    function optionsObjectTest(x, y, opts) {
        opts = opts || {}; // default to an empty options object

        var stringValue = opts.stringValue || "string default value";
        var boolValue = !!opts.boolValue; // coerces value to boolean with a double negation pattern
        var numericValue = opts.numericValue === undefined ? 123 : opts.numericValue;

        return "{x:" + x + ", y:" + y + ", stringValue:'" + stringValue + "', boolValue:" + boolValue + ", numericValue:" + numericValue + "}";

}

here是关于如何使用选项对象

的示例

答案 11 :(得分:4)

<强>引言

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

Function overloading Definition Function Length property Function argument property

最简单形式的

Function overloading意味着函数根据传递给它的参数数量执行不同的任务。值得注意的是,TASK1,TASK2和TASK3在下面突出显示,并且是根据传递给同一函数arguments的{​​{1}}的数量执行的。

fooYo

注意 - JS不提供内置的函数重载功能。

<强>替代

John E Resig(JS的创建者)指出了一种替代方法,它使用上述先决条件来实现实现函数重载的能力。

以下代码使用// 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 if-else语句,使用简单但天真的方法。

  • 评估switch属性。
  • 不同的值会导致调用不同的函数。

&#13;
&#13;
argument-length
&#13;
&#13;
&#13;

另一种技术更加干净和动态。这项技术的亮点是var ninja = { whatever: function() { switch (arguments.length) { case 0: /* do something */ break; case 1: /* do something else */ break; case 2: /* do yet something else */ break; //and so on ... } } }泛型函数。

  • 我们定义了一个函数addMethod,用于向具有相同名称不同功能的对象添加不同的函数。

  • addMethod函数下面的
  • 接受三个参数对象名addMethod,函数名object和我们想要调用的函数name

  • 内部fn定义addMethod存储对封闭帮助下存储的前一个var old的引用 - 保护性气泡。

&#13;
&#13;
function
&#13;
&#13;
&#13;

  • 使用调试器来理解代码流。
  • 下面的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调用时,参数个数ninja.whatever(x)可以是任何颜色,即空白或一个或多个调用不同的函数同时使用x函数。

&#13;
&#13;
addMethod
&#13;
&#13;
&#13;

答案 12 :(得分:4)

无法在javascript中运行重载功能。 所以,我推荐使用typeof()方法代替以下方法 多重函数伪造重载。

function multiTypeFunc(param)
{
    if(typeof param == 'string') {
        alert("I got a string type parameter!!");
     }else if(typeof param == 'number') {
        alert("I got a number type parameter!!");
     }else if(typeof param == 'boolean') {
        alert("I got a boolean type parameter!!");
     }else if(typeof param == 'object') {
        alert("I got a object type parameter!!");
     }else{
        alert("error : the parameter is undefined or null!!");
     }
}
祝你好运!

答案 13 :(得分:4)

另一种解决方法是使用特殊变量: arguments ,这是一个实现:

function sum() {
    var x = 0;
    for (var i = 0; i < arguments.length; ++i) {
        x += arguments[i];
    }
    return x;
}

因此您可以将此代码修改为:

function sum(){
    var s = 0;
    if (typeof arguments[0] !== "undefined") s += arguments[0];
.
.
.
    return s;
}

答案 14 :(得分:3)

检查一下。这很酷。 http://ejohn.org/blog/javascript-method-overloading/ 欺骗Javascript,允许你做这样的调用:

var users = new Users();
users.find(); // Finds all
users.find("John"); // Finds users by name
users.find("John", "Resig"); // Finds users by first and last name

答案 15 :(得分:2)

您现在可以在ECMAScript 2018中执行函数重载而无需使用polyfills,检查var长度/类型等。只需使用spread syntax

function foo(var1, var2, opts){
  // set default values for parameters
  const defaultOpts = {
    a: [1,2,3],
    b: true,
    c: 0.3289,
    d: "str",
  }
  // merge default and passed-in parameters
  // defaultOpts must go first!
  const mergedOpts = {...defaultOpts, ...opts};

  // you can now refer to parameters like b as mergedOpts.b,
  // or just assign mergedOpts.b to b
  console.log(mergedOpts.a);
  console.log(mergedOpts.b);
  console.log(mergedOpts.c);  
  console.log(mergedOpts.d);
}
// the parameters you passed in override the default ones
// all JS types are supported: primitives, objects, arrays, functions, etc.
let var1, var2="random var";
foo(var1, var2, {a: [1,2], d: "differentString"});

// parameter values inside foo:
//a: [1,2]
//b: true
//c: 0.3289
//d: "differentString"

什么是传播语法?

  

ECMAScript建议的“休息/扩展属性”(阶段4)将扩展属性添加到对象文字中。它将自己的可枚举属性从提供的对象复制到新对象。   More on mdn

注意:对象文字中的传播语法在Edge和IE中不起作用,这是一项实验性功能。 see browser compatability

答案 16 :(得分:2)

默认参数未超载,但它可能解决了开发人员在此领域中遇到的一些问题。输入严格由顺序决定,您不能像经典重载中那样随意重新排序:

function transformer(
    firstNumber = 1,
    secondNumber = new Date().getFullYear(),
    transform = function multiply(firstNumber, secondNumber) {
        return firstNumber * secondNumber;
    }
) {
    return transform(firstNumber, secondNumber);
}

console.info(transformer());
console.info(transformer(8));
console.info(transformer(2, 6));
console.info(transformer(undefined, 65));

function add(firstNumber, secondNumber) {
    return firstNumber + secondNumber;
}
console.info(transformer(undefined, undefined, add));
console.info(transformer(3, undefined, add));

结果(2020年):

2020
16160
12
65
2021
2023

更多信息:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters

答案 17 :(得分:2)

由于这篇文章已经包含了许多不同的解决方案,我想我会发布另一个解决方案。

function onlyUnique(value, index, self) {
    return self.indexOf(value) === index;
}

function overload() {
   var functions = arguments;
   var nroffunctionsarguments = [arguments.length];
    for (var i = 0; i < arguments.length; i++) {
        nroffunctionsarguments[i] = arguments[i].length;
    }
    var unique = nroffunctionsarguments.filter(onlyUnique);
    if (unique.length === arguments.length) {
        return function () {
            var indexoffunction = nroffunctionsarguments.indexOf(arguments.length);
            return functions[indexoffunction].apply(this, arguments);
        }
    }
    else throw new TypeError("There are multiple functions with the same number of parameters");

}

可以使用如下所示:

var createVector = overload(
        function (length) {
            return { x: length / 1.414, y: length / 1.414 };
        },
        function (a, b) {
            return { x: a, y: b };
        },
        function (a, b,c) {
            return { x: a, y: b, z:c};
        }
    );
console.log(createVector(3, 4));
console.log(createVector(3, 4,5));
console.log(createVector(7.07));

这个解决方案并不完美,但我只是想证明它是如何完成的。

答案 18 :(得分:2)

通过动态多态性在100行JS

中重载函数

这是来自更大的代码体,包括isFnisArr等类型检查函数。下面的VanillaJS版本已经过重新设计以删除所有外部依赖项,但是您必须定义自己的类型检查函数,以便在.add()调用中使用。

注意:这是一个自动执行的函数(因此我们可以有一个闭包/闭合范围),因此赋值给window.overload而不是function overload() {...}

window.overload = function () {
    "use strict"

    var a_fnOverloads = [],
        _Object_prototype_toString = Object.prototype.toString
    ;

    function isFn(f) {
        return (_Object_prototype_toString.call(f) === '[object Function]');
    } //# isFn

    function isObj(o) {
        return !!(o && o === Object(o));
    } //# isObj

    function isArr(a) {
        return (_Object_prototype_toString.call(a) === '[object Array]');
    } //# isArr

    function mkArr(a) {
        return Array.prototype.slice.call(a);
    } //# mkArr

    function fnCall(fn, vContext, vArguments) {
        //# <ES5 Support for array-like objects
        //#     See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply#Browser_compatibility
        vArguments = (isArr(vArguments) ? vArguments : mkArr(vArguments));

        if (isFn(fn)) {
            return fn.apply(vContext || this, vArguments);
        }
    } //# fnCall

    //# 
    function registerAlias(fnOverload, fn, sAlias) {
        //# 
        if (sAlias && !fnOverload[sAlias]) {
            fnOverload[sAlias] = fn;
        }
    } //# registerAlias

    //# 
    function overload(vOptions) {
        var oData = (isFn(vOptions) ?
                { default: vOptions } :
                (isObj(vOptions) ?
                    vOptions :
                    {
                        default: function (/*arguments*/) {
                            throw "Overload not found for arguments: [" + mkArr(arguments) + "]";
                        }
                    }
                )
            ),
            fnOverload = function (/*arguments*/) {
                var oEntry, i, j,
                    a = arguments,
                    oArgumentTests = oData[a.length] || []
                ;

                //# Traverse the oArgumentTests for the number of passed a(rguments), defaulting the oEntry at the beginning of each loop
                for (i = 0; i < oArgumentTests.length; i++) {
                    oEntry = oArgumentTests[i];

                    //# Traverse the passed a(rguments), if a .test for the current oArgumentTests fails, reset oEntry and fall from the a(rgument)s loop
                    for (j = 0; j < a.length; j++) {
                        if (!oArgumentTests[i].tests[j](a[j])) {
                            oEntry = undefined;
                            break;
                        }
                    }

                    //# If all of the a(rgument)s passed the .tests we found our oEntry, so break from the oArgumentTests loop
                    if (oEntry) {
                        break;
                    }
                }

                //# If we found our oEntry above, .fn.call its .fn
                if (oEntry) {
                    oEntry.calls++;
                    return fnCall(oEntry.fn, this, a);
                }
                //# Else we were unable to find a matching oArgumentTests oEntry, so .fn.call our .default
                else {
                    return fnCall(oData.default, this, a);
                }
            } //# fnOverload
        ;

        //# 
        fnOverload.add = function (fn, a_vArgumentTests, sAlias) {
            var i,
                bValid = isFn(fn),
                iLen = (isArr(a_vArgumentTests) ? a_vArgumentTests.length : 0)
            ;

            //# 
            if (bValid) {
                //# Traverse the a_vArgumentTests, processinge each to ensure they are functions (or references to )
                for (i = 0; i < iLen; i++) {
                    if (!isFn(a_vArgumentTests[i])) {
                        bValid = _false;
                    }
                }
            }

            //# If the a_vArgumentTests are bValid, set the info into oData under the a_vArgumentTests's iLen
            if (bValid) {
                oData[iLen] = oData[iLen] || [];
                oData[iLen].push({
                    fn: fn,
                    tests: a_vArgumentTests,
                    calls: 0
                });

                //# 
                registerAlias(fnOverload, fn, sAlias);

                return fnOverload;
            }
            //# Else one of the passed arguments was not bValid, so throw the error
            else {
                throw "poly.overload: All tests must be functions or strings referencing `is.*`.";
            }
        }; //# overload*.add

        //# 
        fnOverload.list = function (iArgumentCount) {
            return (arguments.length > 0 ? oData[iArgumentCount] || [] : oData);
        }; //# overload*.list

        //# 
        a_fnOverloads.push(fnOverload);
        registerAlias(fnOverload, oData.default, "default");

        return fnOverload;
    } //# overload

    //# 
    overload.is = function (fnTarget) {
        return (a_fnOverloads.indexOf(fnTarget) > -1);
    } //# overload.is

    return overload;
}();

用法:

调用者通过为overload()的返回值分配变量来定义其重载函数。由于链接,可以串联定义额外的重载:

var myOverloadedFn = overload(function(){ console.log("default", arguments) })
    .add(function(){ console.log("noArgs", arguments) }, [], "noArgs")
    .add(function(){ console.log("str", arguments) }, [function(s){ return typeof s === 'string' }], "str")
;

overload()的单个可选参数定义了无法识别签名时要调用的“默认”函数。 .add()的论据是:

  1. fnfunction定义了重载;
  2. a_vArgumentTestsArrayfunction定义了要在arguments上运行的测试。每个function接受一个参数,并根据参数是否有效返回true;
  3. sAlias(可选):string定义直接访问重载函数(fn)的别名,例如myOverloadedFn.noArgs()将直接调用该函数,避免参数的动态多态性测试。
  4. 这种实现实际上不仅允许传统的函数重载,因为a_vArgumentTests的第二个.add()参数在实践中定义了自定义类型。因此,您可以不仅基于类型,而且基于范围,值或值集合来引用参数!

    如果查看overload()的145行代码,您会看到每个签名都按照传递给它的arguments的数量进行分类。这样做是为了限制我们正在运行的测试数量。我还跟踪通话计数。使用一些额外的代码,可以重新排序重载函数的数组,以便首先测试更常用的函数,再次添加一些性能增强措施。

    现在,有一些注意事项......由于Javascript的输入类型很宽松,因此vArgumentTests integer可能会被float验证为window.overload=function(){'use strict';function b(n){return'[object Function]'===m.call(n)}function c(n){return!!(n&&n===Object(n))}function d(n){return'[object Array]'===m.call(n)}function e(n){return Array.prototype.slice.call(n)}function g(n,p,q){if(q=d(q)?q:e(q),b(n))return n.apply(p||this,q)}function h(n,p,q){q&&!n[q]&&(n[q]=p)}function k(n){var p=b(n)?{default:n}:c(n)?n:{default:function(){throw'Overload not found for arguments: ['+e(arguments)+']'}},q=function(){var r,s,t,u=arguments,v=p[u.length]||[];for(s=0;s<v.length;s++){for(r=v[s],t=0;t<u.length;t++)if(!v[s].tests[t](u[t])){r=void 0;break}if(r)break}return r?(r.calls++,g(r.fn,this,u)):g(p.default,this,u)};return q.add=function(r,s,t){var u,v=b(r),w=d(s)?s.length:0;if(v)for(u=0;u<w;u++)b(s[u])||(v=_false);if(v)return p[w]=p[w]||[],p[w].push({fn:r,tests:s,calls:0}),h(q,r,t),q;throw'poly.overload: All tests must be functions or strings referencing `is.*`.'},q.list=function(r){return 0<arguments.length?p[r]||[]:p},l.push(q),h(q,p.default,'default'),q}var l=[],m=Object.prototype.toString;return k.is=function(n){return-1<l.indexOf(n)},k}(); ,因此您必须小心谨慎

    JSCompress.com版本(1114字节,744字节g-zipped):

    [1,2,3].each(&:to_s)
    

答案 19 :(得分:2)

您可以使用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 );
    };
}

我还创建了一种替代此方法的方法,该方法使用缓存来保存函数的变体。 The differencies are described here

// 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);
  };
}

答案 20 :(得分:2)

转发模式=&gt;关于JS重载的最佳实践

转发到另一个名称是从第3个&amp;第四点:

  
      
  1. 使用参数数量
  2.   
  3. 检查参数类型
  4.   
window['foo_'+arguments.length+'_'+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments)

您案件的申请:

 function foo(){
          return window['foo_'+arguments.length+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments);

  }
   //------Assuming that `x` , `y` and `z` are String when calling `foo` . 

  /**-- for :  foo(x)*/
  function foo_1_string(){
  }
  /**-- for : foo(x,y,z) ---*/
  function foo_3_string_string_string(){

  }

其他复杂样本:

      function foo(){
          return window['foo_'+arguments.length+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments);
       }

        /** one argument & this argument is string */
      function foo_1_string(){

      }
       //------------
       /** one argument & this argument is object */
      function foo_1_object(){

      }
      //----------
      /** two arguments & those arguments are both string */
      function foo_2_string_string(){

      }
       //--------
      /** Three arguments & those arguments are : id(number),name(string), callback(function) */
      function foo_3_number_string_function(){
                let args=arguments;
                  new Person(args[0],args[1]).onReady(args[3]);
      }

       //--- And so on ....   

答案 21 :(得分:1)

第一个选项确实值得关注,因为我在相当复杂的代码设置中遇到了这个问题。所以,我的回答是

  
      
  1. 首先使用不同的名称
  2.   

有一点但必不可少的提示,计算机的名称应该看起来不同,但不适合你。名称重载函数,如:func,func1,func2。

答案 22 :(得分:1)

可以通过类似的方法来实现函数重载。

function addCSS(el, prop, val) {
  return {
    2: function() {
      // when two arguments are set
      // now prop is an oject
      for (var i in prop) {
          el.style[i] = prop[i];
      }
    },
    3: function() {
      // when three arguments are set
      el.style[prop] = val;
    }
    }[arguments.length]();
}
// usage
var el = document.getElementById("demo");
addCSS(el, "color", "blue");
addCSS(el, {
    "backgroundColor": "black",
  "padding": "10px"
});

Source

答案 23 :(得分:1)

对于您的用例,这就是我用ES6解决问题的方法(因为它已经在2017年底了):

const foo = (x, y, z) => {
  if (y && z) {
    // Do your foo(x, y, z); functionality
    return output;
  }
  // Do your foo(x); functionality
  return output;
}

显然,您可以将其调整为适用于任意数量的参数,并相应地更改条件语句。

答案 24 :(得分:1)

截至2017年7月,以下是常用技术。请注意,我们还可以在函数中执行类型检查。

function f(...rest){   // rest is an array
   console.log(rest.length);
   for (v of rest) if (typeof(v)=="number")console.log(v);
}
f(1,2,3);  // 3 1 2 3

答案 25 :(得分:1)

JavaScript是无类型语言,我认为根据参数的数量重载方法/函数是有意义的。因此,我建议检查参数是否已定义:

myFunction = function(a, b, c) {
     if (b === undefined && c === undefined ){
          // do x...
     }
     else {
          // do y...
     }
};

答案 26 :(得分:1)

这是一个老问题,但我认为需要另一个条目(尽管我怀疑任何人都会阅读它)。立即调用函数表达式(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,如果它正好传递两个参数ab,则记录两个参数的内容,如果{old函数调用arguments.length !== 2函数1}}。这种模式可以重复任意次,以赋予一个变量几个不同的功能定义。

答案 27 :(得分:0)

JavaScript中的函数重载:

函数重载是编程语言创建具有不同实现方式的同名多个函数的能力。当调用重载函数时,它将运行函数,该函数的特定实现适合于调用的上下文。此上下文通常是接收到的参数数量,它允许一个函数调用根据上下文而有所不同。

JavaScript 没有没有内置函数重载。但是,可以以多种方式模拟此行为。这是一个方便的简单方法:

function sayHi(a, b) {
  console.log('hi there ' + a);
  if (b) { console.log('and ' + b) } // if the parameter is present, execute the block
}

sayHi('Frank', 'Willem');

在不知道要获取多少参数的情况下,可以使用 rest运算符,它是三个点...。它将剩余的参数转换为数组。不过要注意浏览器的兼容性。这是一个示例:

function foo (a, ...b) {
  console.log(b);
}

foo(1,2,3,4);
foo(1,2);

答案 28 :(得分:0)

您可以使用此方法

function func1(a, b, c) {
  console.log(arguments[0]);
  // expected output: 1

  console.log(arguments[1]);
  // expected output: 2

  console.log(arguments[2]);
  // expected output: 3
}

func1(1, 2, 3);

ref:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments

答案 29 :(得分:0)

“打字稿手册”提到Overloads。虽然前面已经提到过检查类型并根据结果执行不同的逻辑,但是这种定义多个命名函数以与键入系统一起使用的方法可能对读者来说很有趣。这是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都会导致错误。

答案 30 :(得分:0)

并非所有人都知道您可以直接在函数签名中执行 Destructuring assignment

因此,您可以轻松定义一个非常灵活的方法签名,恕我直言,它比 Java 方法重载优越。

示例:

const myFunction = (({a, b, c}) => {
    console.log(a, b, c);
});

myFunction({a: 1, b: 2});
myFunction({a: 1, b: 2, c: 3});

你甚至不需要尊重参数的顺序,调用语句和目标方法签名之间存在命名一致性。

答案 31 :(得分:0)

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: 使用休息(传播)参数

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 ...
}

答案 32 :(得分:0)

多年来,我一直在使用此功能来美化我的过载:

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 );
    } );
  }

为性能进行优化时,许多重载函数几乎无法读取。您必须解密函数的标题。这可能比使用我提供的overload函数要快;但是,从人的角度来看,在确定调用哪个过载方面要慢一些。

答案 33 :(得分:0)

我喜欢@AntouanK的方法。我经常发现自己提供的参数和类型不同的函数。有时他们不遵守订单。我用它来查看参数的类型:

findUDPServers: function(socketProperties, success, error) {
    var fqnMap = [];

    fqnMap['undefined'] = fqnMap['function'] = function(success, error) {
        var socketProperties = {name:'HELLO_SERVER'};

        this.searchServers(socketProperties, success, error);
    };

    fqnMap['object'] = function(socketProperties, success, error) {
        var _socketProperties = _.merge({name:'HELLO_SERVER'}, socketProperties || {});

        this.searchServers(_socketProperties, success, error);
    };

    fqnMap[typeof arguments[0]].apply(this, arguments);
}

答案 34 :(得分:0)

我想分享一个有用的超载方法示例。

function Clear(control)
{
  var o = typeof control !== "undefined" ? control : document.body;
  var children = o.childNodes;
  while (o.childNodes.length > 0)
    o.removeChild(o.firstChild);
}

使用方法:   明确(); //清除所有文件

清除(myDiv); //清除myDiv引用的面板

答案 35 :(得分:0)

所以我真的很喜欢这种做事,我在javascript忍者的秘密中找到了

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将重载函数添加到任何对象。这段代码对我来说主要的混淆是使用fn.length == arguments.length - 这是有效的,因为fn.length是预期参数的数量,而arguments.length是实际调用的参数数量。功能。匿名函数没有参数的原因是因为你可以在javascript中传入任意数量的参数并且语言是宽容的。

我喜欢这个,因为你可以在任何地方使用它 - 只需创建这个函数,只需在你想要的任何代码库中使用该方法。

它还避免了一个非常大的if / switch语句,如果你开始编写复杂的代码就会变得难以阅读(接受的答案会导致这种情况)。

就缺点而言,我认为代码最初有点模糊......但我不确定其他人?

答案 36 :(得分:0)

我们让over.js解决这个问题是一种非常优雅的方式。你可以这样做:

var obj = {

  /**
   * Says something in the console.
   *
   * say(msg) - Says something once.
   * say(msg, times) - Says something many times.
   */
  say: Over(
    function(msg$string){
      console.info(msg$string);
    },
    function(msg$string, times$number){
      for (var i = 0; i < times$number; i++) this.say(msg$string);
    }
  )

};

答案 37 :(得分:-1)

我正在开发一个为Javascript提供类代码功能的类的库,目前它支持构造函数,继承,方法超过params的数量以及params,mixins,statics属性和singleton的类型。

请参阅使用该库重载方法的示例:

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

欢迎任何反馈,错误修复,文档和测试改进!

https://github.com/eutsiv/eutsiv.js