Javascript 如何在javascript中重载函数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10855908/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 03:15:10  来源:igfitidea点击:

How to overload functions in javascript?

javascript

提问by Travis J

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 //codeinside of those conditional statements can cause tricky situations with scopes.

传入重载会很快导致函数变得过于冗长,因为每个可能的重载都需要一个条件语句。使用函数来完成//code这些条件语句的内部可能会导致作用域出现棘手的情况。

回答by jfriend00

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.

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

  2. 默认参数- 如果未传递参数,您可以为其定义默认值。

  3. 命名参数- 参数顺序变得无关紧要,您只需命名要传递给函数的参数即可。

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 没有对参数或所需参数数量进行类型检查,因此您可以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. You can check the total quantity or arguments with arguments.length.
  3. You can check the type of any given argument.
  4. For variable numbers of arguments, you can use the argumentspseudo-array to access any given argument with arguments[i].
  1. 您可以通过检查声明的参数名称值是否为 来检查任何给定参数的存在undefined
  2. 您可以使用 来检查总量或参数arguments.length
  3. 您可以检查任何给定参数的类型。
  4. 对于可变数量的参数,您可以使用arguments伪数组访问任何给定的参数arguments[i]

Here are some examples:

这里有些例子:

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

我们来看看jQuery的obj.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()是字符串而第二个参数是undefined,则调用者必须使用这种形式。



// 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, "<></>");

        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 及更高版本中,您实际上可以传递 Map 对象本身。

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 (prop in obj)或通过将所有属性放入数组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, endand stepproperties on an object passed to the selectEntries()function.

这将创建默认属性和值startendstep传递到一个对象的属性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 上进一步描述可以在此处使用的方式。

回答by zzzzBov

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 everyargument 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,如果有人将超过 2 个参数传递给foo,它就会失败,所以更好的形式是使用模块模式和一些检查:

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

当然,您的重载可能想要使用动态数量的参数,因此您可以为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:

我个人的偏好往往是switch,尽管它确实增加了主功能。我将使用此技术的一个常见示例是访问器/修改器方法:

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

回答by Aniket Thakur

You cannot do method overloading in strict sense. Not like the way it is supported in javaor 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 javascriptyou 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 switchthan just if-elsestatements.

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

(more details) PS: Above link goes to my personal blog that has additional details on this.

更多详细信息) PS:上面的链接转到我的个人博客,其中包含有关此的更多详细信息。

回答by Murtnowski

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

回答by Valentin Rusk

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 :)

回答by stamat

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

回答by John Fawcett

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"

回答by Abdennour TOUMI

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

JS重载没问题,pb如何在重载函数时保持干净的代码?

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

您可以使用转发来获得干净的代码,基于两件事:

  1. Number of arguments (when calling the function).
  2. Type of arguments (when calling 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 ....   
    
  1. 参数数量(调用函数时)。
  2. 参数类型(调用函数时)

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

回答by Ralph

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.

基本上在你的类中,你给你想要重载的函数编号,然后通过一个函数调用添加函数重载,快速而简单。

回答by Vlad Bezden

Since JavaScript doesn't have function overload options objectcan 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 没有函数重载,因此可以使用选项对象来代替。如果有一两个必需的参数,最好将它们与选项对象分开。这是关于如何使用选项对象和填充值作为默认值的示例,以防选项对象中未传递值。

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 + "}";

}

}

hereis an example on how to use options object

是一个关于如何使用选项对象的示例