Pregunta

¿Cuál es la mejor manera (s) de falsificar la sobrecarga de funciones en Javascript?

Sé que no es posible sobrecargar funciones en Javascript como en otros idiomas. Si necesitaba una función con dos usos foo (x) y foo (x, y, z) , que es la forma mejor / preferida:

  1. Usando diferentes nombres en primer lugar
  2. Usando argumentos opcionales como y = y || 'predeterminado'
  3. Usando número de argumentos
  4. Verificación de tipos de argumentos
  5. ¿O cómo?
¿Fue útil?

Solución

La mejor forma de sobrecargar funciones con parámetros es no verificar la longitud del argumento o los tipos; comprobar los tipos hará que su código sea más lento y se divertirá con matrices, nulos, objetos, etc.

Lo que la mayoría de los desarrolladores hacen es agregar un objeto como último argumento a sus métodos. Este objeto puede contener cualquier cosa.

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

Entonces puedes manejarlo como quieras en tu método. [Cambiar, si no, etc.]

Otros consejos

A menudo hago esto:

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

Equivalente de 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

Este ejemplo particular es en realidad más elegante en javascript que C #. Los parámetros que no se especifican son 'indefinidos' en javascript, que se evalúa como falso en una declaración if. Sin embargo, la definición de la función no transmite la información de que p2 y p3 son opcionales. Si necesita mucha sobrecarga, jQuery ha decidido utilizar un objeto como parámetro, por ejemplo, jQuery.ajax (opciones). Estoy de acuerdo con ellos en que este es el enfoque más poderoso y claramente documentable para la sobrecarga, pero rara vez necesito más de uno o dos parámetros opcionales rápidos.

EDITAR: prueba IF modificada por sugerencia de Ian

No hay sobrecarga de funciones reales en JavaScript ya que permite pasar cualquier número de parámetros de cualquier tipo. Debe verificar dentro de la función cuántos argumentos y qué escriba lo que son.

La respuesta correcta es NO HAY SOBRECARGA EN JAVASCRIPT.

Verificar / Cambiar dentro de la función no es SOBRECARGA.

El concepto de sobrecarga: En algunos lenguajes de programación, la sobrecarga de funciones o la sobrecarga de métodos es la capacidad de crear múltiples métodos del mismo nombre con diferentes implementaciones. Las llamadas a una función sobrecargada ejecutarán una implementación específica de esa función apropiada para el contexto de la llamada, permitiendo que una llamada de función realice diferentes tareas dependiendo del contexto.

Por ejemplo, doTask () y doTask (objeto O) son métodos sobrecargados. Para llamar a este último, se debe pasar un objeto como parámetro, mientras que el primero no requiere un parámetro y se llama con un campo de parámetro vacío. Un error común sería asignar un valor predeterminado al objeto en el segundo método, lo que resultaría en un error de llamada ambiguo, ya que el compilador no sabría cuál de los dos métodos utilizar.

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

Todas las implementaciones sugeridas son geniales, pero a decir verdad, no hay una implementación nativa para JavaScript.

Hay dos formas de abordar esto mejor:

  1. Pase un diccionario (matriz asociativa) si desea dejar mucha flexibilidad

  2. Tome un objeto como argumento y use una herencia basada en prototipos para agregar flexibilidad.

Aquí hay un enfoque que permite la sobrecarga de métodos reales utilizando los tipos de parámetros, que se muestran a continuación:

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

Editar (2018) : desde que esto se escribió en 2011, la velocidad de las llamadas a métodos directos ha aumentado considerablemente, mientras que la velocidad de los métodos sobrecargados no.

No es un enfoque que recomendaría, pero vale la pena pensar en cómo resolver este tipo de problemas.


Aquí hay un punto de referencia de los diferentes enfoques: https://jsperf.com/function-overloading. Muestra que la sobrecarga de funciones (teniendo en cuenta los tipos) puede ser 13 veces más lenta en Google Chrome's V8 a partir de 16.0 (beta) .

Además de pasar un objeto (es decir, {x: 0, y: 0} ), también se puede adoptar el enfoque C cuando sea apropiado, nombrando los métodos en consecuencia. Por ejemplo, Vector.AddVector (vector), Vector.AddIntegers (x, y, z, ...) y Vector.AddArray (integerArray). Puede mirar las bibliotecas C, como OpenGL para nombrar la inspiración.

Editar : agregué un punto de referencia para pasar un objeto y probar el objeto utilizando tanto 'param' en arg como arg.hasOwnProperty (' param ') , y la sobrecarga de funciones es mucho más rápida que pasar un objeto y verificar las propiedades (al menos en este punto de referencia).

Desde una perspectiva de diseño, la sobrecarga de funciones solo es válida o lógica si los parámetros sobrecargados corresponden a la misma acción. Por lo tanto, es lógico que deba existir un método subyacente que solo se ocupe de detalles específicos, de lo contrario, eso podría indicar elecciones de diseño inapropiadas. Por lo tanto, también se podría resolver el uso de la sobrecarga de funciones mediante la conversión de datos a un objeto respectivo. Por supuesto, uno debe considerar el alcance del problema, ya que no es necesario hacer diseños elaborados si su intención es solo imprimir un nombre, pero para el diseño de marcos y bibliotecas tal pensamiento está justificado.

Mi ejemplo proviene de una implementación de Rectángulo, de ahí la mención de Dimensión y Punto. Quizás Rectangle podría agregar un método GetRectangle () al prototipo Dimension y Point , y luego se resuelve el problema de sobrecarga de funciones. ¿Y qué hay de los primitivos? Bueno, tenemos una longitud de argumento, que ahora es una prueba válida ya que los objetos tienen un método 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);

La mejor manera realmente depende de la función y los argumentos. Cada una de sus opciones es una buena idea en diferentes situaciones. Por lo general, pruebo estos en el siguiente orden hasta que uno de ellos funciona:

  1. Uso de argumentos opcionales como y = y || 'predeterminado'. Esto es conveniente si puede hacerlo, pero puede que no siempre funcione de manera práctica, p. cuando 0 / nulo / indefinido sería un argumento válido.

  2. Uso de varios argumentos. Similar a la última opción, pero puede funcionar cuando el # 1 no funciona.

  3. Verificación de tipos de argumentos. Esto puede funcionar en algunos casos donde el número de argumentos es el mismo. Si no puede determinar los tipos de manera confiable, es posible que deba usar nombres diferentes.

  4. Uso de diferentes nombres en primer lugar. Puede que tenga que hacer esto si las otras opciones no funcionan, no son prácticas o por coherencia con otras funciones relacionadas.

  

Si necesito una función con dos usos foo (x) y foo (x, y, z), ¿cuál es la mejor / preferida forma?

El problema es que JavaScript NO admite de forma nativa la sobrecarga de métodos. Entonces, si ve / analiza dos o más funciones con los mismos nombres, simplemente considerará la última función definida y sobrescribirá las anteriores.

Una de las formas en que creo que es adecuada para la mayoría de los casos es la siguiente:

Digamos que tienes método

function foo(x)
{
} 

En lugar de sobrecargar el método que no es posible en JavaScript , puede definir un nuevo método

fooNew(x,y,z)
{
}

y luego modifique la primera función de la siguiente manera:

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

Si tiene muchos de estos métodos sobrecargados, considere usar switch que solo if-else declaraciones.

( más detalles )

PD: el enlace de arriba va a mi blog personal que tiene detalles adicionales.

No estoy seguro de las mejores prácticas, pero así es como lo hago:

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

Acabo de probar esto, tal vez se adapte a tus necesidades. Dependiendo del número de argumentos, puede acceder a una función diferente. Lo inicializa la primera vez que lo llama. Y el mapa de funciones está oculto en el cierre.

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

Pruébalo.

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

Dado que JavaScript no tiene funciones, el objeto de opciones de sobrecarga puede usarse en su lugar. Si hay uno o dos argumentos obligatorios, es mejor mantenerlos separados del objeto de opciones. Aquí hay un ejemplo sobre cómo usar el objeto de opciones y los valores poblados al valor predeterminado en caso de que el valor no se haya pasado en el objeto de opciones.

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

}

aquí es un ejemplo sobre cómo usar el objeto de opciones

No hay forma de funcionar con sobrecarga en javascript. Por lo tanto, recomiendo lo siguiente por el método typeof () en lugar de Función múltiple para falsificar la sobrecarga.

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

¡Buena suerte!

INTRODUCCIÓN

Hasta ahora, leer tantas respuestas le daría dolor de cabeza a cualquiera. Cualquiera que intente conocer el concepto necesitaría conocer los siguientes requisitos previos .

Definición de sobrecarga de funciones , Propiedad de longitud de función , Propiedad de argumento de función

Sobrecarga de funciones en su forma más simple significa que una función realiza diferentes tareas en función del número de argumentos que se le pasan. En particular, TASK1, TASK2 y TASK3 se resaltan a continuación y se realizan sobre la base del número de argumentos que se pasan a la misma función fooYo .

// if we have a function defined below
function fooYo(){
     // do something here
}
// on invoking fooYo with different number of arguments it should be capable to do different things

fooYo();  // does TASK1
fooYo('sagar'); // does TASK2
fooYo('sagar','munjal'); // does TAKS3

NOTA - JS no proporciona la capacidad incorporada de sobrecarga de funciones.

Alternativa

John E Resig (creador de JS) ha señalado una alternativa que utiliza los requisitos previos anteriores para lograr la capacidad de implementar la sobrecarga de funciones.

El siguiente código utiliza un enfoque sencillo pero ingenuo al usar la declaración if-else o switch .

  • evalúa la propiedad argumento-length .
  • valores diferentes resultan en la invocación de diferentes funciones.

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

Otra técnica es mucho más limpia y dinámica. Lo más destacado de esta técnica es la función genérica addMethod .

  • definimos una función addMethod que se usa para agregar diferentes funciones a un objeto con el mismo nombre pero funcionalidades diferentes .

  • debajo de la función addMethod acepta tres parámetros nombre de objeto object , nombre de función name y la función que queremos que se invoque fn .

  • Dentro de addMethod definición var old almacena la referencia a la función anterior almacenada con la ayuda del cierre, una burbuja protectora.

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

  • usa el depurador para comprender el flujo del código.
  • debajo del addMethod agrega tres funciones que cuando se invoca usando ninja.whatever (x) con el número de argumentos x que pueden ser cualquier cosa es decir, ya sea en blanco o una o más de una, invoca diferentes funciones tal como se definen al hacer uso de la función addMethod .

var ninja = {};
debugger;


addMethod(ninja,'whatever',function(){ console.log("I am the one with ZERO arguments supplied") });
addMethod(ninja,'whatever',function(a){ console.log("I am the one with ONE arguments supplied") });
addMethod(ninja,'whatever',function(a,b){ console.log("I am the one with TWO arguments supplied") });


ninja.whatever();
ninja.whatever(1,2);
ninja.whatever(3);

Otra forma de abordar esto es mediante el uso de la variable especial: argumentos , esta es una implementación:

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

para que pueda modificar este código a:

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

mira esto. Esta muy padre. http://ejohn.org/blog/javascript-method-overloading/ Truco Javascript para permitirle hacer llamadas como esta:

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

Como esta publicación ya contiene muchas soluciones diferentes, pensé que publicaría otra.

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

}

esto se puede usar como se muestra a continuación:

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

Esta solución no es perfecta, pero solo quiero demostrar cómo se puede hacer.

Puede usar el 'addMethod' de John Resig. Con este método puede "sobrecargar" métodos basados ??en recuento de argumentos.

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

También he creado una alternativa a este método que utiliza el almacenamiento en caché para contener las variaciones de la función. Las diferencias se describen aquí

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

Patrón de reenvío = > la mejor práctica en sobrecarga de JS

Reenviar a otra función cuyo nombre se construye a partir de la tercera & amp; 4to puntos:

  
      
  1. Usando número de argumentos
  2.   
  3. Verificación de tipos de argumentos
  4.   
window['foo_'+arguments.length+'_'+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments)

Solicitud en su caso:

 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(){

  }

Otra muestra compleja:

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

Sobrecarga de funciones mediante polimorfismo dinámico en 100 líneas de JS

Esto es de un cuerpo de código más grande que incluye las funciones de verificación de tipo isFn , isArr , etc. La versión de VanillaJS a continuación ha sido modificada para eliminar todas las dependencias externas, sin embargo, tendrá que definir sus propias funciones de verificación de tipo para usar en las llamadas .add () .

Nota: esta es una función de ejecución automática (por lo que podemos tener un alcance cerrado / cerrado), de ahí la asignación a window.overload en lugar de función sobrecarga () {...} .

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

Uso:

La persona que llama define sus funciones sobrecargadas asignando una variable al retorno de overload () . Gracias al encadenamiento, las sobrecargas adicionales se pueden definir en serie:

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

El argumento opcional único para overload () define el " default " función para llamar si no se puede identificar la firma. Los argumentos para .add () son:

  1. fn : función que define la sobrecarga;
  2. a_vArgumentTests : Array de function s que define las pruebas a ejecutar en los argumentos . Cada función acepta un único argumento y devuelve true thy en función de si el argumento es válido;
  3. sAlias ?? (Opcional): string que define el alias para acceder directamente a la función de sobrecarga ( fn ), p. myOverloadedFn.noArgs () llamará a esa función directamente, evitando las pruebas de polimorfismo dinámico de los argumentos.

Esta implementación en realidad permite más que simples sobrecargas de funciones tradicionales, ya que el segundo argumento a_vArgumentTests para .add () en la práctica define tipos personalizados. Por lo tanto, ¡podría abrir argumentos no solo en función del tipo, sino también de rangos, valores o colecciones de valores!

Si mira a través de las 145 líneas de código para overload () verá que cada firma se clasifica por la cantidad de argumentos que se le pasan. Esto se hace para limitar el número de pruebas que estamos ejecutando. También llevo un registro del recuento de llamadas. Con algún código adicional, las matrices de funciones sobrecargadas podrían reorganizarse para que las funciones más comúnmente llamadas se prueben primero, agregando nuevamente alguna medida de mejora del rendimiento.

Ahora, hay algunas advertencias ... Como Javascript está mal escrito, deberá tener cuidado con sus vArgumentTests ya que un entero podría validarse como un < code> float , etc.

Versión JSCompress.com (1114 bytes, 744 bytes g-zipped):

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

Ahora puede hacer una sobrecarga de funciones en ECMAScript 2018 sin polyfills, verificando la longitud / tipo de var, etc., solo use sintaxis de propagación .

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"

¿Qué es la sintaxis de propagación?

  

Las propiedades Rest / Spread para la propuesta ECMAScript (etapa 4) agrega propiedades de propagación a los literales de objeto. Copia propiedades enumerables propias de un objeto proporcionado en un nuevo objeto.    Más información sobre mdn

Nota: la sintaxis extendida en literales de objetos no funciona en Edge e IE y es una característica experimental. vea la compatibilidad del navegador

La primera opción realmente merece atención porque es lo que he encontrado en una configuración de código bastante compleja. Entonces, mi respuesta es

  
      
  1. Usando diferentes nombres en primer lugar
  2.   

Con una pequeña pero esencial pista, los nombres deberían ser diferentes para la computadora, pero no para ti. Nombre funciones sobrecargadas como: func, func1, func2.

Esta es una vieja pregunta, pero creo que necesita otra entrada (aunque dudo que alguien la lea). El uso de Expresiones de funciones invocadas inmediatamente (IIFE) se puede usar junto con cierres y funciones en línea para permitir la sobrecarga de funciones. Considere el siguiente ejemplo (artificial):

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

En resumen, el uso de IIFE crea un ámbito local, lo que nos permite definir la variable privada old para almacenar una referencia a la definición inicial de la función foo . Esta función devuelve una función en línea newFoo que registra el contenido de ambos argumentos si se pasa exactamente dos argumentos a y b o llama al antigua funciona si argumentos.length! == 2 . Este patrón se puede repetir cualquier cantidad de veces para dotar a una variable con varias definiciones funcionales diferentes.

JavaScript es un lenguaje sin tipo, y solo creo que tiene sentido sobrecargar un método / función con respecto al número de parámetros. Por lo tanto, recomendaría verificar si el parámetro ha sido definido:

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

A partir de julio de 2017, lo siguiente ha sido la técnica común. Tenga en cuenta que también podemos realizar verificaciones de tipo dentro de la función.

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

Para su caso de uso, así es como lo abordaría con ES6 (ya que es finales de 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;
}

Obviamente, puede adaptar esto para trabajar con cualquier cantidad de parámetros y simplemente cambiar sus declaraciones condicionales en consecuencia.

Se puede hacer algo así para la sobrecarga de funciones.

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

Fuente

Hicimos over.js para resolver este problema es una manera muy elegante. Puedes hacer:

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

};

Así que realmente me gustó esta forma de hacer las cosas que encontré en los secretos del ninja de 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);
    }
  }
}

luego usa addMethod para agregar funciones sobrecargadas a cualquier objeto. La principal confusión en este código para mí fue el uso de fn.length == argumentos.length: esto funciona porque fn.length es el número de parámetros esperados, mientras argumentos.length es el número de parámetros que realmente se llaman con el función. La razón por la cual la función anónima no tiene argumento es porque puede pasar cualquier número de argumentos en JavaScript y el lenguaje es indulgente.

Me gustó esto porque puedes usarlo en todas partes, solo crea esta función y simplemente usa el método en la base de código que desees.

También evita tener una declaración if / switch ridículamente grande, que se vuelve difícil de leer si comienza a escribir código complejo (la respuesta aceptada resultará en esto).

En términos de contras, supongo que el código es inicialmente un poco oscuro ... ¿pero no estoy seguro de los demás?

Me gustaría compartir un ejemplo útil de un enfoque de tipo sobrecargado.

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

Uso:   Claro(); // Borra todo el documento

Borrar (myDiv); // Borra el panel al que hace referencia myDiv

Me gusta el enfoque de @ AntouanK. A menudo me encuentro ofreciendo una función con diferentes números o parámetros y diferentes tipos. A veces no siguen una orden. Utilizo para mapear buscando los tipos de parámetros:

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);
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top