如何在javascript中重载函数?

时间:2022-09-27 13:44:33

Classical (non-js) approach to overloading:

经典(非js)超载方法:

function myFunc(){
 //code
}

function myFunc(overloaded){
 //other code
}

Javascript wont let more than one function be defined with the same name. As such, things like this show up:

Javascript不会让多个函数用相同的名称来定义。因此,像这样的事情出现了:

function myFunc(options){
 if(options["overloaded"]){
  //code
 }
}

Is there a better workaround for function overloading in javascript other than passing an object with the overloads in it?

除了传递包含重载的对象之外,javascript中是否还有更好的方法来处理函数重载?

Passing in overloads can quickly cause a function to become too verbose because each possible overload would then need a conditional statement. Using functions to accomplish the //code inside of those conditional statements can cause tricky situations with scopes.

传入重载会很快导致函数变得过于冗长,因为每个可能的重载都需要一个条件语句。使用函数来完成这些条件语句中的// /代码可能会导致作用域的复杂情况。

13 个解决方案

#1


95  

There are multiple aspects to argument overloading in Javascript:

Javascript中的参数重载有很多方面:

  1. Variable arguments - You can pass different sets of arguments (in both type and quantity) and the function will behave in a way that matches the arguments passed to it.

    变量参数——您可以传递不同的参数集(类型和数量),函数的行为将与传递给它的参数相匹配。

  2. Default arguments - You can define a default value for an argument if it is not passed.

    默认参数——如果参数没有通过,您可以为其定义一个默认值。

  3. Named arguments - Argument order becomes irrelevant and you just name which arguments you want to pass to the function.

    命名参数—参数顺序变得不相关,您只需命名要传递给函数的参数。

Below is a section on each of these categories of argument handling.

下面是关于这些类型的参数处理的一节。

Variable Arguments

Because javascript has no type checking on arguments or required qty of arguments, you can just have one implementation of myFunc() that can adapt to what arguments were passed to it by checking the type, presence or quantity of arguments.

因为javascript没有对参数进行类型检查,也没有对参数进行要求的qty,所以只能有myFunc()的一个实现,它可以通过检查参数的类型、存在或数量来适应传递给它的参数。

jQuery does this all the time. You can make some of the arguments optional or you can branch in your function depending upon what arguments are passed to it.

jQuery总是这么做。您可以选择一些参数,也可以根据传递给它的参数在函数中进行分支。

In implementing these types of overloads, you have several different techniques you can use:

在实现这些类型的重载时,您可以使用几种不同的技术:

  1. You can check for the presence of any given argument by checking to see if the declared argument name value is undefined.
  2. 您可以通过检查声明的参数名称值是否未定义来检查任何给定参数的存在。
  3. You can check the total quantity or arguments with arguments.length.
  4. 您可以使用argumts .length检查总数或参数。
  5. You can check the type of any given argument.
  6. 您可以检查任何给定参数的类型。
  7. For variable numbers of arguments, you can use the arguments pseudo-array to access any given argument with arguments[i].
  8. 对于可变数量的参数,您可以使用参数伪数组来使用参数[i]访问任何给定的参数。

Here are some examples:

下面是一些例子:

Let's look at jQuery's obj.data() method. It supports four different forms of usage:

让我们看看jQuery的object .data()方法。它支持四种不同的用法:

obj.data("key");
obj.data("key", value);
obj.data();
obj.data(object);

Each one triggers a different behavior and, without using this dynamic form of overloading, would require four separate functions.

每一个触发一个不同的行为,如果不使用这种动态重载形式,将需要四个独立的函数。

Here's how one can discern between all these options in English and then I'll combine them all in code:

下面是如何区分英语中的所有选项,然后我用代码将它们结合起来:

// get the data element associated with a particular key value
obj.data("key");

If the first argument passed to .data() is a string and the second argument is undefined, then the caller must be using this form.

如果传递给.data()的第一个参数是一个字符串,而第二个参数没有定义,那么调用者必须使用这个表单。


// set the value associated with a particular key
obj.data("key", value);

If the second argument is not undefined, then set the value of a particular key.

如果第二个参数不是未定义的,那么设置一个特定键的值。


// get all keys/values
obj.data();

If no arguments are passed, then return all keys/values in a returned object.

如果没有传递参数,则返回返回对象中的所有键/值。


// set all keys/values from the passed in object
obj.data(object);

If the type of the first argument is a plain object, then set all keys/values from that object.

如果第一个参数的类型是一个普通对象,则设置该对象的所有键/值。


Here's how you could combine all of those in one set of javascript logic:

以下是如何将所有这些组合到一组javascript逻辑中:

 // method declaration for .data()
 data: function(key, value) {
     if (arguments.length === 0) {
         // .data()
         // no args passed, return all keys/values in an object
     } else if (typeof key === "string") {
         // first arg is a string, look at type of second arg
         if (typeof value !== "undefined") {
             // .data("key", value)
             // set the value for a particular key
         } else {
             // .data("key")
             // retrieve a value for a key
         }
     } else if (typeof key === "object") {
         // .data(object)
         // set all key/value pairs from this object
     } else {
         // unsupported arguments passed
     }
 },

The key to this technique is to make sure that all forms of arguments you want to accept are uniquely identifiable and there is never any confusion about which form the caller is using. This generally requires ordering the arguments appropriately and making sure that there is enough uniqueness in the type and position of the arguments that you can always tell which form is being used.

此技术的关键是确保您想要接受的所有类型的参数都是惟一可识别的,并且对于调用者使用的是哪种形式没有任何混淆。这通常需要适当地排序参数,并确保参数的类型和位置具有足够的唯一性,您总是可以判断哪些表单正在被使用。

For example, if you have a function that takes three string arguments:

例如,如果有一个函数接受三个字符串参数:

obj.query("firstArg", "secondArg", "thirdArg");

You can easily make the third argument optional and you can easily detect that condition, but you cannot make only the second argument optional because you can't tell which of these the caller means to be passing because there is no way to identify if the second argument is meant to be the second argument or the second argument was omitted so what's in the second argument's spot is actually the third argument:

您可以很容易地将第三个参数可选的,您可以很容易地检测到条件,但是你不能只可选的第二个参数,因为你不能告诉哪一个调用者传递意味着因为没有办法确定如果第二个参数是第二个参数,第二个参数是省略的第二个参数的位置实际上是第三个参数:

obj.query("firstArg", "secondArg");
obj.query("firstArg", "thirdArg");

Since all three arguments are the same type, you can't tell the difference between different arguments so you don't know what the caller intended. With this calling style, only the third argument can be optional. If you wanted to omit the second argument, it would have to be passed as null (or some other detectable value) instead and your code would detect that:

因为这三个参数都是相同的类型,所以您不能区分不同的参数,所以您不知道调用者想要什么。使用这种调用样式,只有第三个参数是可选的。如果要省略第二个参数,则必须将其作为null(或其他可检测的值)传递,您的代码将检测到:

obj.query("firstArg", null, "thirdArg");

Here's a jQuery example of optional arguments. both arguments are optional and take on default values if not passed:

下面是一个jQuery可选参数示例。两个参数都是可选的,如果不通过,则取默认值:

clone: function( dataAndEvents, deepDataAndEvents ) {
    dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
    deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;

    return this.map( function () {
        return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
    });
},

Here's a jQuery example where the argument can be missing or any one of three different types which gives you four different overloads:

下面是一个jQuery示例,其中的参数可能会丢失,或者三种不同类型中的任何一种会给您带来四种不同的重载:

html: function( value ) {
    if ( value === undefined ) {
        return this[0] && this[0].nodeType === 1 ?
            this[0].innerHTML.replace(rinlinejQuery, "") :
            null;

    // See if we can take a shortcut and just use innerHTML
    } else if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
        (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
        !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {

        value = value.replace(rxhtmlTag, "<$1></$2>");

        try {
            for ( var i = 0, l = this.length; i < l; i++ ) {
                // Remove element nodes and prevent memory leaks
                if ( this[i].nodeType === 1 ) {
                    jQuery.cleanData( this[i].getElementsByTagName("*") );
                    this[i].innerHTML = value;
                }
            }

        // If using innerHTML throws an exception, use the fallback method
        } catch(e) {
            this.empty().append( value );
        }

    } else if ( jQuery.isFunction( value ) ) {
        this.each(function(i){
            var self = jQuery( this );

            self.html( value.call(this, i, self.html()) );
        });

    } else {
        this.empty().append( value );
    }

    return this;
},

Named Arguments

Other languages (like Python) allow one to pass named arguments as a means of passing only some arguments and making the arguments independent of the order they are passed in. Javascript does not directly support the feature of named arguments. A design pattern that is commonly used in its place is to pass a map of properties/values. This can be done by passing an object with properties and values or in ES6 and above, you could actually pass a Map object itself.

其他语言(如Python)允许一个通过指定的参数作为传递一些参数的方法,并使参数独立于传入的顺序。Javascript不直接支持命名参数的特性。通常在其位置使用的设计模式是传递属性/值的映射。这可以通过传递具有属性和值的对象来实现,或者在ES6或以上版本中,您实际上可以传递映射对象本身。

Here's a simple ES5 example:

这里有一个简单的ES5示例:

jQuery's $.ajax() accepts a form of usage where you just pass it a single parameter which is a regular Javascript object with properties and values. Which properties you pass it determine which arguments/options are being passed to the ajax call. Some may be required, many are optional. Since they are properties on an object, there is no specific order. In fact, there are more than 30 different properties that can be passed on that object, only one (the url) is required.

jQuery的$.ajax()接受一种使用形式,您只需向它传递一个参数,这是一个具有属性和值的常规Javascript对象。传递给它的属性决定传递给ajax调用的参数/选项。有些可能是必需的,许多是可选的。因为它们是对象上的属性,所以没有特定的顺序。事实上,有超过30种不同的属性可以传递到该对象上,只有一个(url)是必需的。

Here's an example:

这里有一个例子:

$.ajax({url: "http://www.example.com/somepath", data: myArgs, dataType: "json"}).then(function(result) {
    // process result here
});

Inside of the $.ajax() implementation, it can then just interrogate which properties were passed on the incoming object and use those as named arguments. This can be done either with for (prop in obj) or by getting all the properties into an array with Object.keys(obj) and then iterating that array.

在$.ajax()实现内部,它可以询问传入对象上传递了哪些属性,并将这些属性用作命名参数。这可以通过for(在obj中是prop)或通过使用Object.keys(obj)将所有属性放到一个数组中,然后迭代该数组来实现。

This technique is used very commonly in Javascript when there are large numbers of arguments and/or many arguments are optional. Note: this puts an onus on the implementating function to make sure that a minimal valid set of arguments is present and to give the caller some debug feedback what is missing if insufficient arguments are passed (probably by throwing an exception with a helpful error message).

当有大量的参数和/或许多参数是可选的时,这种技术通常在Javascript中使用。注意:这给实现函数增加了一个责任,以确保出现了最小有效的参数集,并在传递不足够的参数时给调用者一些调试反馈(可能通过抛出异常并提供有用的错误消息)。

In an ES6 environment, it is possible to use destructuring to create default properties/values for the above passed object. This is discussed in more detail in this reference article.

在ES6环境中,可以使用析构来为上面传递的对象创建默认属性/值。本文将更详细地讨论这一点。

Here's one example from that article:

这是那篇文章中的一个例子:

function selectEntries({ start=0, end=-1, step=1 } = {}) {
    ···
};

This creates default properties and values for the start, end and step properties on an object passed to the selectEntries() function.

这将为传递给selectEntries()函数的对象的开始、结束和步骤属性创建默认属性和值。

Default values for function arguments

In ES6, Javascript adds built-in language support for default values for arguments.

在ES6中,Javascript为参数添加了默认值的内置语言支持。

For example:

例如:

function multiply(a, b = 1) {
  return a*b;
}

multiply(5); // 5

Further description of the ways this can be used here on MDN.

进一步描述在MDN上可以使用它的方式。

#2


30  

Overloading a function in JavaScript can be done in many ways. All of them involve a single master function that either performs all the processes, or delegates to sub-functions/processes.

在JavaScript中重载函数可以通过多种方式实现。它们都涉及一个主函数,该函数可以执行所有进程,也可以委托给子函数/进程。

One of the most common simple techniques involves a simple switch:

最常见的一种简单的技术包括一个简单的开关:

function foo(a, b) {
    switch (arguments.length) {
    case 0:
        //do basic code
        break;
    case 1:
        //do code with `a`
        break;
    case 2:
    default:
        //do code with `a` & `b`
        break;
    }
}

A more elegant technique would be to use an array (or object if you're not making overloads for every argument count):

一种更优雅的技术是使用数组(或对象,如果您没有为每个参数计数造成重载):

fooArr = [
    function () {
    },
    function (a) {
    },
    function (a,b) {
    }
];
function foo(a, b) {
    return fooArr[arguments.length](a, b);
}

That previous example isn't very elegant, anyone could modify fooArr, and it would fail if someone passes in more than 2 arguments to foo, so a better form would be to use a module pattern and a few checks:

之前的例子不是很优雅,任何人都可以修改fooArr,如果有人向foo传递超过2个参数,它就会失败,所以更好的形式是使用模块模式和一些检查:

var foo = (function () {
    var fns;
    fns = [
        function () {
        },
        function (a) {
        },
        function (a, b) {
        }
    ];
    function foo(a, b) {
        var fnIndex;
        fnIndex = arguments.length;
        if (fnIndex > foo.length) {
            fnIndex = foo.length;
        }
        return fns[fnIndex].call(this, a, b);
    }
    return foo;
}());

Of course your overloads might want to use a dynamic number of parameters, so you could use an object for the fns collection.

当然,重载可能需要使用动态数量的参数,因此可以为fns集合使用对象。

var foo = (function () {
    var fns;
    fns = {};
    fns[0] = function () {
    };
    fns[1] = function (a) {
    };
    fns[2] = function (a, b) {
    };
    fns.params = function (a, b /*, params */) {
    };
    function foo(a, b) {
        var fnIndex;
        fnIndex = arguments.length;
        if (fnIndex > foo.length) {
            fnIndex = 'params';
        }
        return fns[fnIndex].apply(this, Array.prototype.slice.call(arguments));
    }
    return foo;
}());

My personal preference tends to be the switch, although it does bulk up the master function. A common example of where I'd use this technique would be a accessor/mutator method:

我个人偏好的是交换机,尽管它确实增加了主功能。我使用这种技术的一个常见例子是访问器/换向器方法:

function Foo() {} //constructor
Foo.prototype = {
    bar: function (val) {
        switch (arguments.length) {
        case 0:
            return this._bar;
        case 1:
            this._bar = val;
            return this;
        }
    }
}

#3


5  

You cannot do method overloading in strict sense. Not like the way it is supported in java or c#.

严格地说,你不能做方法重载。不像java或c#所支持的那样。

The issue is that JavaScript does NOT natively support method overloading. So, if it sees/parses two or more functions with a same names it’ll just consider the last defined function and overwrite the previous ones.

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

One of the way I think is suitable for most of the case is follows -

我认为适用于大多数情况的一种方法是—

Lets say you have method

假设你有方法

function foo(x)
{
} 

Instead of overloading method which is not possible in javascript you can define a new method

可以定义一个新方法,而不是在javascript中不可能重载方法

fooNew(x,y,z)
{
}

and then modify the 1st function as follows -

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

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

If you have many such overloaded method consider using switch than just if-else statements.

如果您有许多这样的重载方法,请考虑使用switch语句而不仅仅是If -else语句。

(more details)

(更多细节)

#4


4  

I am using a bit different overloading approach based on arguments number. However i believe John Fawcett's approach is also good. Here the example, code based on John Resig's (jQuery's Author) explanations.

我使用了一种基于参数数的稍微不同的重载方法。但是我相信约翰·福塞特的方法也不错。这里的示例是基于John Resig (jQuery作者)解释的代码。

// o = existing object, n = function name, f = function.
    function overload(o, n, f){
        var old = o[n];
        o[n] = function(){
            if(f.length == arguments.length){
                return f.apply(this, arguments);
            }
            else if(typeof o == 'function'){
                return old.apply(this, arguments);
            }
        };
    }

usability:

可用性:

var obj = {};
overload(obj, 'function_name', function(){ /* what we will do if no args passed? */});
overload(obj, 'function_name', function(first){ /* what we will do if 1 arg passed? */});
overload(obj, 'function_name', function(first, second){ /* what we will do if 2 args passed? */});
overload(obj, 'function_name', function(first,second,third){ /* what we will do if 3 args passed? */});
//... etc :)

#5


3  

I tried to develop an elegant solution to this problem described here. And you can find the demo here. The usage looks like this:

我试图为这里描述的这个问题开发一个优雅的解决方案。你可以在这里找到演示。用法如下:

var out = def({
    'int': function(a) {
        alert('Here is int '+a);
    },

    'float': function(a) {
        alert('Here is float '+a);
    },

    'string': function(a) {
        alert('Here is string '+a);
    },

    'int,string': function(a, b) {
        alert('Here is an int '+a+' and a string '+b);
    },
    'default': function(obj) {
        alert('Here is some other value '+ obj);
    }

});

out('ten');
out(1);
out(2, 'robot');
out(2.5);
out(true);

The methods used to achieve this:

实现这一目标的方法如下:

var def = function(functions, parent) {
 return function() {
    var types = [];
    var args = [];
    eachArg(arguments, function(i, elem) {
        args.push(elem);
        types.push(whatis(elem));
    });
    if(functions.hasOwnProperty(types.join())) {
        return functions[types.join()].apply(parent, args);
    } else {
        if (typeof functions === 'function')
            return functions.apply(parent, args);
        if (functions.hasOwnProperty('default'))
            return functions['default'].apply(parent, args);        
    }
  };
};

var eachArg = function(args, fn) {
 var i = 0;
 while (args.hasOwnProperty(i)) {
    if(fn !== undefined)
        fn(i, args[i]);
    i++;
 }
 return i-1;
};

var whatis = function(val) {

 if(val === undefined)
    return 'undefined';
 if(val === null)
    return 'null';

 var type = typeof val;

 if(type === 'object') {
    if(val.hasOwnProperty('length') && val.hasOwnProperty('push'))
        return 'array';
    if(val.hasOwnProperty('getDate') && val.hasOwnProperty('toLocaleTimeString'))
        return 'date';
    if(val.hasOwnProperty('toExponential'))
        type = 'number';
    if(val.hasOwnProperty('substring') && val.hasOwnProperty('length'))
        return 'string';
 }

 if(type === 'number') {
    if(val.toString().indexOf('.') > 0)
        return 'float';
    else
        return 'int';
 }

 return type;
};

#6


3  

https://github.com/jrf0110/leFunc

https://github.com/jrf0110/leFunc

var getItems = leFunc({
  "string": function(id){
    // Do something
  },
  "string,object": function(id, options){
    // Do something else
  },
  "string,object,function": function(id, options, callback){
    // Do something different
    callback();
  },
  "object,string,function": function(options, message, callback){
    // Do something ca-raaaaazzzy
    callback();
  }
});

getItems("123abc"); // Calls the first function - "string"
getItems("123abc", {poop: true}); // Calls the second function - "string,object"
getItems("123abc", {butt: true}, function(){}); // Calls the third function - "string,object,function"
getItems({butt: true}, "What what?" function(){}); // Calls the fourth function - "object,string,function"

#7


3  

In javascript you can implement the function just once and invoke the function without the parameters myFunc() You then check to see if options is 'undefined'

在javascript中,只需实现一次函数并调用函数,无需参数myFunc(),然后检查选项是否“未定义”

function myFunc(options){
 if(typeof options != 'undefined'){
  //code
 }
}

#8


2  

Check this out:

看看这个:

http://www.codeproject.com/Articles/688869/Overloading-JavaScript-Functions

http://www.codeproject.com/Articles/688869/Overloading-JavaScript-Functions

Basically in your class, you number your functions that you want to be overloaded and then with one function call you add function overloading, fast and easy.

基本上在你的类中,你的函数是你想要重载的,然后一个函数调用你添加函数重载,快速和简单。

#9


2  

Since JavaScript doesn't have function overload options object can be used instead. If there are one or two required arguments, it's better to keep them separate from the options object. Here is an example on how to use options object and populated values to default value in case if value was not passed in options object.

因为JavaScript没有函数重载选项,所以可以使用对象。如果有一两个必需的参数,最好将它们与options对象分开。这里有一个示例,说明如何在选项对象中不传递值时,将选项对象和填充值使用为默认值。

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 is an example on how to use options object

下面是如何使用options对象的示例

#10


2  

For this you need to create a function that adds the function to an object, then it will execute depending on the amount of arguments you send to the function:

为此,您需要创建一个向对象添加函数的函数,然后它将根据您向函数发送的参数数量执行:

<script > 
//Main function to add the methods
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 ninjas = {
   values: ["Dean Edwards", "Sam Stephenson", "Alex Russell"]
};

//Here we declare the first function with no arguments passed
  addMethod(ninjas, "find", function(){
    return this.values;
});

//Second function with one argument
  addMethod(ninjas, "find", function(name){
    var ret = [];
    for (var i = 0; i < this.values.length; i++)
      if (this.values[i].indexOf(name) == 0)
        ret.push(this.values[i]);
    return ret;
  });

//Third function with two arguments
  addMethod(ninjas, "find", function(first, last){
    var ret = [];
    for (var i = 0; i < this.values.length; i++)
      if (this.values[i] == (first + " " + last))
        ret.push(this.values[i]);
    return ret;
  });


//Now you can do:
ninjas.find();
ninjas.find("Sam");
ninjas.find("Dean", "Edwards")
</script>

#11


2  

No Problem with Overloading in JS , The pb how to maintain a clean code when overloading function ?

You can use a forward to have clean code , based on two things:

您可以使用forward来获得干净的代码,基于以下两点:

  1. Number of arguments (when calling the function).
  2. 参数的数量(在调用函数时)。
  3. Type of arguments (when callin the function)

    参数类型(在调用函数时)

      function myFunc(){
          return window['myFunc_'+arguments.length+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments);
       }
    
        /** one argument & this argument is string */
      function myFunc_1_string(){
    
      }
       //------------
       /** one argument & this argument is object */
      function myFunc_1_object(){
    
      }
      //----------
      /** two arguments & those arguments are both string */
      function myFunc_2_string_string(){
    
      }
       //--------
      /** Three arguments & those arguments are : id(number),name(string), callback(function) */
      function myFunc_3_number_string_function(){
                let args=arguments;
                  new Person(args[0],args[1]).onReady(args[3]);
      }
    
       //--- And so on ....   
    

#12


0  

I like to add sub functions within a parent function to achieve the ability to differentiate between argument groups for the same functionality.

我喜欢在父函数中添加子函数,以实现对相同功能的参数组进行区分的能力。

var doSomething = function() {
    var foo;
    var bar;
};

doSomething.withArgSet1 = function(arg0, arg1) {
    var obj = new doSomething();
    // do something the first way
    return obj;
};

doSomething.withArgSet2 = function(arg2, arg3) {
    var obj = new doSomething();
    // do something the second way
    return obj;
};

#13


0  

What you are trying to achieve is best done using the function's local arguments variable.

您试图实现的是最好使用函数的局部参数变量。

function foo() {
    if (arguments.length === 0) {
        //do something
    }
    if (arguments.length === 1) {
        //do something else
    }
}

foo(); //do something
foo('one'); //do something else

You can find a better explanation of how this works here.

你可以在这里找到更好的解释。

#1


95  

There are multiple aspects to argument overloading in Javascript:

Javascript中的参数重载有很多方面:

  1. Variable arguments - You can pass different sets of arguments (in both type and quantity) and the function will behave in a way that matches the arguments passed to it.

    变量参数——您可以传递不同的参数集(类型和数量),函数的行为将与传递给它的参数相匹配。

  2. Default arguments - You can define a default value for an argument if it is not passed.

    默认参数——如果参数没有通过,您可以为其定义一个默认值。

  3. Named arguments - Argument order becomes irrelevant and you just name which arguments you want to pass to the function.

    命名参数—参数顺序变得不相关,您只需命名要传递给函数的参数。

Below is a section on each of these categories of argument handling.

下面是关于这些类型的参数处理的一节。

Variable Arguments

Because javascript has no type checking on arguments or required qty of arguments, you can just have one implementation of myFunc() that can adapt to what arguments were passed to it by checking the type, presence or quantity of arguments.

因为javascript没有对参数进行类型检查,也没有对参数进行要求的qty,所以只能有myFunc()的一个实现,它可以通过检查参数的类型、存在或数量来适应传递给它的参数。

jQuery does this all the time. You can make some of the arguments optional or you can branch in your function depending upon what arguments are passed to it.

jQuery总是这么做。您可以选择一些参数,也可以根据传递给它的参数在函数中进行分支。

In implementing these types of overloads, you have several different techniques you can use:

在实现这些类型的重载时,您可以使用几种不同的技术:

  1. You can check for the presence of any given argument by checking to see if the declared argument name value is undefined.
  2. 您可以通过检查声明的参数名称值是否未定义来检查任何给定参数的存在。
  3. You can check the total quantity or arguments with arguments.length.
  4. 您可以使用argumts .length检查总数或参数。
  5. You can check the type of any given argument.
  6. 您可以检查任何给定参数的类型。
  7. For variable numbers of arguments, you can use the arguments pseudo-array to access any given argument with arguments[i].
  8. 对于可变数量的参数,您可以使用参数伪数组来使用参数[i]访问任何给定的参数。

Here are some examples:

下面是一些例子:

Let's look at jQuery's obj.data() method. It supports four different forms of usage:

让我们看看jQuery的object .data()方法。它支持四种不同的用法:

obj.data("key");
obj.data("key", value);
obj.data();
obj.data(object);

Each one triggers a different behavior and, without using this dynamic form of overloading, would require four separate functions.

每一个触发一个不同的行为,如果不使用这种动态重载形式,将需要四个独立的函数。

Here's how one can discern between all these options in English and then I'll combine them all in code:

下面是如何区分英语中的所有选项,然后我用代码将它们结合起来:

// get the data element associated with a particular key value
obj.data("key");

If the first argument passed to .data() is a string and the second argument is undefined, then the caller must be using this form.

如果传递给.data()的第一个参数是一个字符串,而第二个参数没有定义,那么调用者必须使用这个表单。


// set the value associated with a particular key
obj.data("key", value);

If the second argument is not undefined, then set the value of a particular key.

如果第二个参数不是未定义的,那么设置一个特定键的值。


// get all keys/values
obj.data();

If no arguments are passed, then return all keys/values in a returned object.

如果没有传递参数,则返回返回对象中的所有键/值。


// set all keys/values from the passed in object
obj.data(object);

If the type of the first argument is a plain object, then set all keys/values from that object.

如果第一个参数的类型是一个普通对象,则设置该对象的所有键/值。


Here's how you could combine all of those in one set of javascript logic:

以下是如何将所有这些组合到一组javascript逻辑中:

 // method declaration for .data()
 data: function(key, value) {
     if (arguments.length === 0) {
         // .data()
         // no args passed, return all keys/values in an object
     } else if (typeof key === "string") {
         // first arg is a string, look at type of second arg
         if (typeof value !== "undefined") {
             // .data("key", value)
             // set the value for a particular key
         } else {
             // .data("key")
             // retrieve a value for a key
         }
     } else if (typeof key === "object") {
         // .data(object)
         // set all key/value pairs from this object
     } else {
         // unsupported arguments passed
     }
 },

The key to this technique is to make sure that all forms of arguments you want to accept are uniquely identifiable and there is never any confusion about which form the caller is using. This generally requires ordering the arguments appropriately and making sure that there is enough uniqueness in the type and position of the arguments that you can always tell which form is being used.

此技术的关键是确保您想要接受的所有类型的参数都是惟一可识别的,并且对于调用者使用的是哪种形式没有任何混淆。这通常需要适当地排序参数,并确保参数的类型和位置具有足够的唯一性,您总是可以判断哪些表单正在被使用。

For example, if you have a function that takes three string arguments:

例如,如果有一个函数接受三个字符串参数:

obj.query("firstArg", "secondArg", "thirdArg");

You can easily make the third argument optional and you can easily detect that condition, but you cannot make only the second argument optional because you can't tell which of these the caller means to be passing because there is no way to identify if the second argument is meant to be the second argument or the second argument was omitted so what's in the second argument's spot is actually the third argument:

您可以很容易地将第三个参数可选的,您可以很容易地检测到条件,但是你不能只可选的第二个参数,因为你不能告诉哪一个调用者传递意味着因为没有办法确定如果第二个参数是第二个参数,第二个参数是省略的第二个参数的位置实际上是第三个参数:

obj.query("firstArg", "secondArg");
obj.query("firstArg", "thirdArg");

Since all three arguments are the same type, you can't tell the difference between different arguments so you don't know what the caller intended. With this calling style, only the third argument can be optional. If you wanted to omit the second argument, it would have to be passed as null (or some other detectable value) instead and your code would detect that:

因为这三个参数都是相同的类型,所以您不能区分不同的参数,所以您不知道调用者想要什么。使用这种调用样式,只有第三个参数是可选的。如果要省略第二个参数,则必须将其作为null(或其他可检测的值)传递,您的代码将检测到:

obj.query("firstArg", null, "thirdArg");

Here's a jQuery example of optional arguments. both arguments are optional and take on default values if not passed:

下面是一个jQuery可选参数示例。两个参数都是可选的,如果不通过,则取默认值:

clone: function( dataAndEvents, deepDataAndEvents ) {
    dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
    deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;

    return this.map( function () {
        return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
    });
},

Here's a jQuery example where the argument can be missing or any one of three different types which gives you four different overloads:

下面是一个jQuery示例,其中的参数可能会丢失,或者三种不同类型中的任何一种会给您带来四种不同的重载:

html: function( value ) {
    if ( value === undefined ) {
        return this[0] && this[0].nodeType === 1 ?
            this[0].innerHTML.replace(rinlinejQuery, "") :
            null;

    // See if we can take a shortcut and just use innerHTML
    } else if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
        (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
        !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {

        value = value.replace(rxhtmlTag, "<$1></$2>");

        try {
            for ( var i = 0, l = this.length; i < l; i++ ) {
                // Remove element nodes and prevent memory leaks
                if ( this[i].nodeType === 1 ) {
                    jQuery.cleanData( this[i].getElementsByTagName("*") );
                    this[i].innerHTML = value;
                }
            }

        // If using innerHTML throws an exception, use the fallback method
        } catch(e) {
            this.empty().append( value );
        }

    } else if ( jQuery.isFunction( value ) ) {
        this.each(function(i){
            var self = jQuery( this );

            self.html( value.call(this, i, self.html()) );
        });

    } else {
        this.empty().append( value );
    }

    return this;
},

Named Arguments

Other languages (like Python) allow one to pass named arguments as a means of passing only some arguments and making the arguments independent of the order they are passed in. Javascript does not directly support the feature of named arguments. A design pattern that is commonly used in its place is to pass a map of properties/values. This can be done by passing an object with properties and values or in ES6 and above, you could actually pass a Map object itself.

其他语言(如Python)允许一个通过指定的参数作为传递一些参数的方法,并使参数独立于传入的顺序。Javascript不直接支持命名参数的特性。通常在其位置使用的设计模式是传递属性/值的映射。这可以通过传递具有属性和值的对象来实现,或者在ES6或以上版本中,您实际上可以传递映射对象本身。

Here's a simple ES5 example:

这里有一个简单的ES5示例:

jQuery's $.ajax() accepts a form of usage where you just pass it a single parameter which is a regular Javascript object with properties and values. Which properties you pass it determine which arguments/options are being passed to the ajax call. Some may be required, many are optional. Since they are properties on an object, there is no specific order. In fact, there are more than 30 different properties that can be passed on that object, only one (the url) is required.

jQuery的$.ajax()接受一种使用形式,您只需向它传递一个参数,这是一个具有属性和值的常规Javascript对象。传递给它的属性决定传递给ajax调用的参数/选项。有些可能是必需的,许多是可选的。因为它们是对象上的属性,所以没有特定的顺序。事实上,有超过30种不同的属性可以传递到该对象上,只有一个(url)是必需的。

Here's an example:

这里有一个例子:

$.ajax({url: "http://www.example.com/somepath", data: myArgs, dataType: "json"}).then(function(result) {
    // process result here
});

Inside of the $.ajax() implementation, it can then just interrogate which properties were passed on the incoming object and use those as named arguments. This can be done either with for (prop in obj) or by getting all the properties into an array with Object.keys(obj) and then iterating that array.

在$.ajax()实现内部,它可以询问传入对象上传递了哪些属性,并将这些属性用作命名参数。这可以通过for(在obj中是prop)或通过使用Object.keys(obj)将所有属性放到一个数组中,然后迭代该数组来实现。

This technique is used very commonly in Javascript when there are large numbers of arguments and/or many arguments are optional. Note: this puts an onus on the implementating function to make sure that a minimal valid set of arguments is present and to give the caller some debug feedback what is missing if insufficient arguments are passed (probably by throwing an exception with a helpful error message).

当有大量的参数和/或许多参数是可选的时,这种技术通常在Javascript中使用。注意:这给实现函数增加了一个责任,以确保出现了最小有效的参数集,并在传递不足够的参数时给调用者一些调试反馈(可能通过抛出异常并提供有用的错误消息)。

In an ES6 environment, it is possible to use destructuring to create default properties/values for the above passed object. This is discussed in more detail in this reference article.

在ES6环境中,可以使用析构来为上面传递的对象创建默认属性/值。本文将更详细地讨论这一点。

Here's one example from that article:

这是那篇文章中的一个例子:

function selectEntries({ start=0, end=-1, step=1 } = {}) {
    ···
};

This creates default properties and values for the start, end and step properties on an object passed to the selectEntries() function.

这将为传递给selectEntries()函数的对象的开始、结束和步骤属性创建默认属性和值。

Default values for function arguments

In ES6, Javascript adds built-in language support for default values for arguments.

在ES6中,Javascript为参数添加了默认值的内置语言支持。

For example:

例如:

function multiply(a, b = 1) {
  return a*b;
}

multiply(5); // 5

Further description of the ways this can be used here on MDN.

进一步描述在MDN上可以使用它的方式。

#2


30  

Overloading a function in JavaScript can be done in many ways. All of them involve a single master function that either performs all the processes, or delegates to sub-functions/processes.

在JavaScript中重载函数可以通过多种方式实现。它们都涉及一个主函数,该函数可以执行所有进程,也可以委托给子函数/进程。

One of the most common simple techniques involves a simple switch:

最常见的一种简单的技术包括一个简单的开关:

function foo(a, b) {
    switch (arguments.length) {
    case 0:
        //do basic code
        break;
    case 1:
        //do code with `a`
        break;
    case 2:
    default:
        //do code with `a` & `b`
        break;
    }
}

A more elegant technique would be to use an array (or object if you're not making overloads for every argument count):

一种更优雅的技术是使用数组(或对象,如果您没有为每个参数计数造成重载):

fooArr = [
    function () {
    },
    function (a) {
    },
    function (a,b) {
    }
];
function foo(a, b) {
    return fooArr[arguments.length](a, b);
}

That previous example isn't very elegant, anyone could modify fooArr, and it would fail if someone passes in more than 2 arguments to foo, so a better form would be to use a module pattern and a few checks:

之前的例子不是很优雅,任何人都可以修改fooArr,如果有人向foo传递超过2个参数,它就会失败,所以更好的形式是使用模块模式和一些检查:

var foo = (function () {
    var fns;
    fns = [
        function () {
        },
        function (a) {
        },
        function (a, b) {
        }
    ];
    function foo(a, b) {
        var fnIndex;
        fnIndex = arguments.length;
        if (fnIndex > foo.length) {
            fnIndex = foo.length;
        }
        return fns[fnIndex].call(this, a, b);
    }
    return foo;
}());

Of course your overloads might want to use a dynamic number of parameters, so you could use an object for the fns collection.

当然,重载可能需要使用动态数量的参数,因此可以为fns集合使用对象。

var foo = (function () {
    var fns;
    fns = {};
    fns[0] = function () {
    };
    fns[1] = function (a) {
    };
    fns[2] = function (a, b) {
    };
    fns.params = function (a, b /*, params */) {
    };
    function foo(a, b) {
        var fnIndex;
        fnIndex = arguments.length;
        if (fnIndex > foo.length) {
            fnIndex = 'params';
        }
        return fns[fnIndex].apply(this, Array.prototype.slice.call(arguments));
    }
    return foo;
}());

My personal preference tends to be the switch, although it does bulk up the master function. A common example of where I'd use this technique would be a accessor/mutator method:

我个人偏好的是交换机,尽管它确实增加了主功能。我使用这种技术的一个常见例子是访问器/换向器方法:

function Foo() {} //constructor
Foo.prototype = {
    bar: function (val) {
        switch (arguments.length) {
        case 0:
            return this._bar;
        case 1:
            this._bar = val;
            return this;
        }
    }
}

#3


5  

You cannot do method overloading in strict sense. Not like the way it is supported in java or c#.

严格地说,你不能做方法重载。不像java或c#所支持的那样。

The issue is that JavaScript does NOT natively support method overloading. So, if it sees/parses two or more functions with a same names it’ll just consider the last defined function and overwrite the previous ones.

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

One of the way I think is suitable for most of the case is follows -

我认为适用于大多数情况的一种方法是—

Lets say you have method

假设你有方法

function foo(x)
{
} 

Instead of overloading method which is not possible in javascript you can define a new method

可以定义一个新方法,而不是在javascript中不可能重载方法

fooNew(x,y,z)
{
}

and then modify the 1st function as follows -

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

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

If you have many such overloaded method consider using switch than just if-else statements.

如果您有许多这样的重载方法,请考虑使用switch语句而不仅仅是If -else语句。

(more details)

(更多细节)

#4


4  

I am using a bit different overloading approach based on arguments number. However i believe John Fawcett's approach is also good. Here the example, code based on John Resig's (jQuery's Author) explanations.

我使用了一种基于参数数的稍微不同的重载方法。但是我相信约翰·福塞特的方法也不错。这里的示例是基于John Resig (jQuery作者)解释的代码。

// o = existing object, n = function name, f = function.
    function overload(o, n, f){
        var old = o[n];
        o[n] = function(){
            if(f.length == arguments.length){
                return f.apply(this, arguments);
            }
            else if(typeof o == 'function'){
                return old.apply(this, arguments);
            }
        };
    }

usability:

可用性:

var obj = {};
overload(obj, 'function_name', function(){ /* what we will do if no args passed? */});
overload(obj, 'function_name', function(first){ /* what we will do if 1 arg passed? */});
overload(obj, 'function_name', function(first, second){ /* what we will do if 2 args passed? */});
overload(obj, 'function_name', function(first,second,third){ /* what we will do if 3 args passed? */});
//... etc :)

#5


3  

I tried to develop an elegant solution to this problem described here. And you can find the demo here. The usage looks like this:

我试图为这里描述的这个问题开发一个优雅的解决方案。你可以在这里找到演示。用法如下:

var out = def({
    'int': function(a) {
        alert('Here is int '+a);
    },

    'float': function(a) {
        alert('Here is float '+a);
    },

    'string': function(a) {
        alert('Here is string '+a);
    },

    'int,string': function(a, b) {
        alert('Here is an int '+a+' and a string '+b);
    },
    'default': function(obj) {
        alert('Here is some other value '+ obj);
    }

});

out('ten');
out(1);
out(2, 'robot');
out(2.5);
out(true);

The methods used to achieve this:

实现这一目标的方法如下:

var def = function(functions, parent) {
 return function() {
    var types = [];
    var args = [];
    eachArg(arguments, function(i, elem) {
        args.push(elem);
        types.push(whatis(elem));
    });
    if(functions.hasOwnProperty(types.join())) {
        return functions[types.join()].apply(parent, args);
    } else {
        if (typeof functions === 'function')
            return functions.apply(parent, args);
        if (functions.hasOwnProperty('default'))
            return functions['default'].apply(parent, args);        
    }
  };
};

var eachArg = function(args, fn) {
 var i = 0;
 while (args.hasOwnProperty(i)) {
    if(fn !== undefined)
        fn(i, args[i]);
    i++;
 }
 return i-1;
};

var whatis = function(val) {

 if(val === undefined)
    return 'undefined';
 if(val === null)
    return 'null';

 var type = typeof val;

 if(type === 'object') {
    if(val.hasOwnProperty('length') && val.hasOwnProperty('push'))
        return 'array';
    if(val.hasOwnProperty('getDate') && val.hasOwnProperty('toLocaleTimeString'))
        return 'date';
    if(val.hasOwnProperty('toExponential'))
        type = 'number';
    if(val.hasOwnProperty('substring') && val.hasOwnProperty('length'))
        return 'string';
 }

 if(type === 'number') {
    if(val.toString().indexOf('.') > 0)
        return 'float';
    else
        return 'int';
 }

 return type;
};

#6


3  

https://github.com/jrf0110/leFunc

https://github.com/jrf0110/leFunc

var getItems = leFunc({
  "string": function(id){
    // Do something
  },
  "string,object": function(id, options){
    // Do something else
  },
  "string,object,function": function(id, options, callback){
    // Do something different
    callback();
  },
  "object,string,function": function(options, message, callback){
    // Do something ca-raaaaazzzy
    callback();
  }
});

getItems("123abc"); // Calls the first function - "string"
getItems("123abc", {poop: true}); // Calls the second function - "string,object"
getItems("123abc", {butt: true}, function(){}); // Calls the third function - "string,object,function"
getItems({butt: true}, "What what?" function(){}); // Calls the fourth function - "object,string,function"

#7


3  

In javascript you can implement the function just once and invoke the function without the parameters myFunc() You then check to see if options is 'undefined'

在javascript中,只需实现一次函数并调用函数,无需参数myFunc(),然后检查选项是否“未定义”

function myFunc(options){
 if(typeof options != 'undefined'){
  //code
 }
}

#8


2  

Check this out:

看看这个:

http://www.codeproject.com/Articles/688869/Overloading-JavaScript-Functions

http://www.codeproject.com/Articles/688869/Overloading-JavaScript-Functions

Basically in your class, you number your functions that you want to be overloaded and then with one function call you add function overloading, fast and easy.

基本上在你的类中,你的函数是你想要重载的,然后一个函数调用你添加函数重载,快速和简单。

#9


2  

Since JavaScript doesn't have function overload options object can be used instead. If there are one or two required arguments, it's better to keep them separate from the options object. Here is an example on how to use options object and populated values to default value in case if value was not passed in options object.

因为JavaScript没有函数重载选项,所以可以使用对象。如果有一两个必需的参数,最好将它们与options对象分开。这里有一个示例,说明如何在选项对象中不传递值时,将选项对象和填充值使用为默认值。

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 is an example on how to use options object

下面是如何使用options对象的示例

#10


2  

For this you need to create a function that adds the function to an object, then it will execute depending on the amount of arguments you send to the function:

为此,您需要创建一个向对象添加函数的函数,然后它将根据您向函数发送的参数数量执行:

<script > 
//Main function to add the methods
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 ninjas = {
   values: ["Dean Edwards", "Sam Stephenson", "Alex Russell"]
};

//Here we declare the first function with no arguments passed
  addMethod(ninjas, "find", function(){
    return this.values;
});

//Second function with one argument
  addMethod(ninjas, "find", function(name){
    var ret = [];
    for (var i = 0; i < this.values.length; i++)
      if (this.values[i].indexOf(name) == 0)
        ret.push(this.values[i]);
    return ret;
  });

//Third function with two arguments
  addMethod(ninjas, "find", function(first, last){
    var ret = [];
    for (var i = 0; i < this.values.length; i++)
      if (this.values[i] == (first + " " + last))
        ret.push(this.values[i]);
    return ret;
  });


//Now you can do:
ninjas.find();
ninjas.find("Sam");
ninjas.find("Dean", "Edwards")
</script>

#11


2  

No Problem with Overloading in JS , The pb how to maintain a clean code when overloading function ?

You can use a forward to have clean code , based on two things:

您可以使用forward来获得干净的代码,基于以下两点:

  1. Number of arguments (when calling the function).
  2. 参数的数量(在调用函数时)。
  3. Type of arguments (when callin the function)

    参数类型(在调用函数时)

      function myFunc(){
          return window['myFunc_'+arguments.length+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments);
       }
    
        /** one argument & this argument is string */
      function myFunc_1_string(){
    
      }
       //------------
       /** one argument & this argument is object */
      function myFunc_1_object(){
    
      }
      //----------
      /** two arguments & those arguments are both string */
      function myFunc_2_string_string(){
    
      }
       //--------
      /** Three arguments & those arguments are : id(number),name(string), callback(function) */
      function myFunc_3_number_string_function(){
                let args=arguments;
                  new Person(args[0],args[1]).onReady(args[3]);
      }
    
       //--- And so on ....   
    

#12


0  

I like to add sub functions within a parent function to achieve the ability to differentiate between argument groups for the same functionality.

我喜欢在父函数中添加子函数,以实现对相同功能的参数组进行区分的能力。

var doSomething = function() {
    var foo;
    var bar;
};

doSomething.withArgSet1 = function(arg0, arg1) {
    var obj = new doSomething();
    // do something the first way
    return obj;
};

doSomething.withArgSet2 = function(arg2, arg3) {
    var obj = new doSomething();
    // do something the second way
    return obj;
};

#13


0  

What you are trying to achieve is best done using the function's local arguments variable.

您试图实现的是最好使用函数的局部参数变量。

function foo() {
    if (arguments.length === 0) {
        //do something
    }
    if (arguments.length === 1) {
        //do something else
    }
}

foo(); //do something
foo('one'); //do something else

You can find a better explanation of how this works here.

你可以在这里找到更好的解释。