Sovraccarico di funzioni in Javascript - Best practice


783

Qual è il modo (i) migliore (i) di falsificare il sovraccarico delle funzioni in Javascript?

So che non è possibile sovraccaricare le funzioni in Javascript come in altre lingue. Se avessi bisogno di una funzione con due usi foo(x)e foo(x,y,z)quale sia il modo migliore / preferito:

  1. Utilizzando nomi diversi in primo luogo
  2. Utilizzando argomenti opzionali come y = y || 'default'
  3. Utilizzando il numero di argomenti
  4. Verifica dei tipi di argomenti
  5. O come?

14
Forse sarebbe utile chiederti perché pensi di aver bisogno del sovraccarico delle funzioni per cominciare. Penso che ci avvicinerà a una soluzione reale.
Breton,

1
Questo è chiuso, ma faccio quanto segue: this.selectBy = {istanza: selectByInstance, // testo della funzione: selectByText, // valore della funzione: selectByValue // Function};
Prigioniero ZERO,

La mia risposta mostra come eseguire il sovraccarico della funzione tempo di esecuzione, ha una penalità di velocità e non consiglierei di farlo per aggirare le specifiche di Javascript. Il sovraccarico di funzioni è davvero un compito di compilazione, fornisco la risposta solo a scopi accademici e la lascio a tua discrezione se impiegarla o meno nel codice.
Keldon Alleyne,

2
Nel caso sia utile, ho creato un framework js leggero che consente il sovraccarico del metodo basato sul tipo. Ovviamente le stesse avvertenze si applicano per quanto riguarda le prestazioni, ma finora ha funzionato bene per le mie esigenze e ha ancora un sacco di spazio per migliorare: blog.pebbl.co.uk/2013/01/describejs.html#methodoverloading
Pebbl

Risposte:


602

Il modo migliore per eseguire il sovraccarico delle funzioni con i parametri non è controllare la lunghezza dell'argomento o i tipi; il controllo dei tipi rallenterà il tuo codice e ti divertirai con array, null, oggetti, ecc.

Quello che fanno la maggior parte degli sviluppatori è puntare su un oggetto come ultimo argomento dei loro metodi. Questo oggetto può contenere qualsiasi 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"});

Quindi puoi gestirlo come desideri nel tuo metodo. [Cambia, if-else, ecc.]


43
Potresti fornire un'implementazione di esempio di foo () che illustra come questi parametri "opts" sono usati / referenziati?
Moe Howard,

24
Moe // Potrebbe essere così; if(opts['test']) //if test param exists, do something.. if(opts['bar']) //if bar param exists, do something
Deckard,

80
Questa non è una funzione di sovraccarico. Il sovraccarico delle funzioni sta avendo due funzioni separate con lo stesso nome ma parametri diversi. Quello che stai descrivendo è solo una funzione con un argomento oggetto alla fine.
d512,

66
@ user1334007 è impossibile avere il sovraccarico delle funzioni come si farebbe in Java / .NET. Sì, questo non è "esattamente" sovraccarico, ma fa il lavoro.
Epascarello

18
Sono sorpreso che nessuno lo abbia già chiesto: perché il controllo arguments.lengthnon è raccomandato? Inoltre, sono stato qui prima e ho letto Quello che fa la maggior parte degli sviluppatori è ... , ma sono sicuro che questo è l'unico posto che ho visto. Quel metodo rovina anche la sugosità sintattica di avere "sovraccarichi"!
c24w,

169

Faccio spesso questo:

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

Questo esempio particolare è in realtà più elegante in JavaScript di C #. I parametri che non vengono specificati sono "non definiti" in JavaScript, che viene considerato falso in un'istruzione if. Tuttavia, la definizione della funzione non trasmette le informazioni che p2 e p3 sono opzionali. Se è necessario un sovraccarico eccessivo, jQuery ha deciso di utilizzare un oggetto come parametro, ad esempio jQuery.ajax (opzioni). Concordo con loro sul fatto che questo è l'approccio più potente e chiaramente documentabile al sovraccarico, ma raramente ho bisogno di più di uno o due parametri opzionali rapidi.

EDIT: modificato il test IF secondo il suggerimento di Ian


16
I parametri che non sono specificati sono undefinedin JS, no null. Come best practice, non dovresti mai impostare nulla undefined, quindi non dovrebbe essere un problema fintanto che cambi il test p2 === undefined.
Tamzin Blake,

3
Se passi falsecome ultimo argomento, non si concatenerà "false"fino alla fine, perché if(p3)non si diramerà.
dreamlax,

5
Solo una breve nota, il tuo typeof p2 === "undefined"è probabilmente il contrario di quello che ti aspetti nell'istanza del tuo esempio, penso che typeof p2 !== "undefined"sia quello che volevi. Inoltre, posso suggerire che si supponga di concatenare la stringa, il numero e il valore booleano che si fa realmentep2 === "number"; p3 === "boolean"
WillFM,

8
Mi piace farlo: p3 = p3 || 'valore predefinito';
Dorian,

3
Qual è il significato di ===e !==? Perché non usare solo ==e !=?
Ricardo Cruz

76

Non esiste un vero sovraccarico di funzioni in JavaScript poiché consente di passare un numero qualsiasi di parametri di qualsiasi tipo. Devi verificare all'interno della funzione quanti argomenti sono stati passati e che tipo sono.


1
John Resig (di jQuery) una volta ha provato a farlo, ma il tentativo è stato puramente accademico e non ha fornito alcun vantaggio reale.
scunliffe,

14
La funzione di John Resig sovraccarica qui ejohn.org/blog/javascript-method-overloading
Terrance

@Terrance: mi piace anche il metodo di Resig. Esso funziona magicamente. Devo solo trovare un modo per creare un test per convalidare i casi d'uso.
chrisvillanueva,

"Questa funzione non cambierà il mondo, ma è breve, concisa e utilizza un'oscura funzione JavaScript, quindi vince nel mio libro." :-)
Frerich Raabe,

68

La risposta corretta è NON ESSERE SOVRACCARICATO IN JAVASCRIPT.

Il controllo / commutazione all'interno della funzione non è SOVRACCARICO.

Il concetto di sovraccarico: in alcuni linguaggi di programmazione, il sovraccarico delle funzioni o il sovraccarico dei metodi è la possibilità di creare più metodi con lo stesso nome con implementazioni diverse. Le chiamate a una funzione sovraccarica eseguiranno un'implementazione specifica di quella funzione appropriata al contesto della chiamata, consentendo a una chiamata di funzione di eseguire diverse attività a seconda del contesto.

Ad esempio, doTask () e doTask (oggetto O) sono metodi sovraccarichi. Per chiamare quest'ultimo, un oggetto deve essere passato come parametro, mentre il primo non richiede un parametro e viene chiamato con un campo di parametro vuoto. Un errore comune sarebbe quello di assegnare un valore predefinito all'oggetto nel secondo metodo, il che comporterebbe un errore di chiamata ambiguo, poiché il compilatore non saprebbe quale dei due metodi utilizzare.

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

Tutte le implementazioni suggerite sono fantastiche, ma a dire il vero, non esiste un'implementazione nativa per JavaScript.


4
finalmente una risposta normale! NON C'È SOVRACCARICO IN JAVASCRIPT.
Razvan Tudorica,

4
OP ha chiesto un modo per simulare un sovraccarico.
Ateur Games,

Come ho detto prima, siamo qui per educare le persone, non solo dare loro risposte senza verificare che ciò che chiedono sia giusto.
Marco,

27

Esistono due modi per affrontarlo meglio:

  1. Passa un dizionario (array associativo) se vuoi lasciare molta flessibilità

  2. Prendi un oggetto come argomento e usa l'ereditarietà basata sul prototipo per aggiungere flessibilità.


1
questo è stato il mio pensiero iniziale, tuttavia, se la funzione che si sta creando deve essere utilizzata in una libreria o da altri, enumerare chiaramente i valori può essere utile
roberthuttinger,

19

Ecco un approccio che consente il sovraccarico del metodo reale utilizzando i tipi di parametro, mostrato di seguito:

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

Modifica (2018) : da quando è stato scritto nel 2011, la velocità delle chiamate ai metodi diretti è notevolmente aumentata mentre la velocità dei metodi sovraccaricati non lo è stata.

Non è un approccio che consiglierei, ma è un esercizio di pensiero utile pensare a come risolvere questi tipi di problemi.


Ecco un benchmark dei diversi approcci: https://jsperf.com/function-overloading . Mostra che il sovraccarico delle funzioni (tenendo conto dei tipi) può essere circa 13 volte più lento nel V8 di Google Chrome a partire dalla 16.0 (beta) .

Oltre a passare un oggetto (cioè {x: 0, y: 0}), si può anche adottare l'approccio C quando appropriato, nominando i metodi di conseguenza. Ad esempio, Vector.AddVector (vector), Vector.AddIntegers (x, y, z, ...) e Vector.AddArray (integerArray). Puoi guardare le librerie C, come OpenGL per ispirare i nomi.

Modifica : ho aggiunto un benchmark per il passaggio di un oggetto e il test per l'oggetto utilizzando entrambi 'param' in arge arg.hasOwnProperty('param'), e il sovraccarico delle funzioni è molto più veloce del passaggio di un oggetto e del controllo delle proprietà (almeno in questo benchmark).

Dal punto di vista del progetto, il sovraccarico delle funzioni è valido o logico solo se i parametri sovraccarichi corrispondono alla stessa azione. Quindi è ovvio che dovrebbe esserci un metodo di base che si occupa solo di dettagli specifici, altrimenti ciò potrebbe indicare scelte di progettazione inadeguate. Quindi si potrebbe anche risolvere l'uso del sovraccarico delle funzioni convertendo i dati in un rispettivo oggetto. Naturalmente si deve considerare la portata del problema in quanto non è necessario realizzare progetti elaborati se la vostra intenzione è solo quella di stampare un nome, ma per la progettazione di quadri e librerie tale pensiero è giustificato.

Il mio esempio viene da un'implementazione di Rectangle - da qui la menzione di Dimensione e Punto. Forse Rettangolo potrebbe aggiungere un GetRectangle()metodo per l' Dimensione Pointprototipi, e poi viene ordinato la funzione di sovraccarico problema. E i primitivi? Bene, abbiamo la lunghezza dell'argomento, che ora è un test valido poiché gli oggetti hanno un GetRectangle()metodo.

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

16

Il modo migliore dipende davvero dalla funzione e dagli argomenti. Ognuna delle tue opzioni è una buona idea in diverse situazioni. In genere li provo nel seguente ordine fino a quando uno di loro funziona:

  1. Utilizzo di argomenti opzionali come y = y || 'predefinito'. Questo è utile se puoi farlo, ma potrebbe non funzionare sempre praticamente, ad esempio quando 0 / null / undefined sarebbe un argomento valido.

  2. Utilizzando il numero di argomenti. Simile all'ultima opzione ma può funzionare quando il numero 1 non funziona.

  3. Verifica dei tipi di argomenti. Questo può funzionare in alcuni casi in cui il numero di argomenti è lo stesso. Se non è possibile determinare in modo affidabile i tipi, potrebbe essere necessario utilizzare nomi diversi.

  4. Utilizzando nomi diversi in primo luogo. Potrebbe essere necessario farlo se le altre opzioni non funzionano, non sono pratiche o per coerenza con altre funzioni correlate.


14

Se avessi bisogno di una funzione con due usi foo (x) e foo (x, y, z) qual è il modo migliore / preferito?

Il problema è che JavaScript NON supporta in modo nativo il sovraccarico dei metodi. Quindi, se vede / analizza due o più funzioni con gli stessi nomi, prenderà semplicemente in considerazione l'ultima funzione definita e sovrascriverà quelle precedenti.

Uno dei modi in cui penso sia adatto per la maggior parte del caso è il seguente:

Diciamo che hai un metodo

function foo(x)
{
} 

Invece di sovraccaricare il metodo che non è possibile in JavaScript è possibile definire un nuovo metodo

fooNew(x,y,z)
{
}

e quindi modificare la prima funzione come segue:

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

Se hai molti di questi metodi sovraccarichi, considera l'utilizzo switchdi semplici if-elseistruzioni.

( maggiori dettagli )

PS: sopra il link va al mio blog personale che ha ulteriori dettagli.


9

Non sono sicuro delle migliori pratiche, ma ecco come lo faccio:

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

2
@Luis: ho aggiunto alcuni commenti, si spera, utili.
chessweb

6

Ho appena provato questo, forse è adatto alle tue esigenze. A seconda del numero degli argomenti, è possibile accedere a una funzione diversa. Lo inizializzi la prima volta che lo chiami. E la mappa delle funzioni è nascosta nella chiusura.

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

Provalo.

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

5

Poiché JavaScript non ha opzioni di sovraccarico della funzione, è possibile utilizzare l'oggetto. Se sono richiesti uno o due argomenti, è meglio tenerli separati dall'oggetto options. Ecco un esempio su come utilizzare l'oggetto opzioni e i valori popolati sul valore predefinito nel caso in cui il valore non sia stato passato nell'oggetto opzioni.

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

}

ecco un esempio su come utilizzare l'oggetto opzioni


4

Non c'è modo di sovraccaricare le funzioni in JavaScript. Quindi, consiglio di seguire il typeof()metodo seguente invece di una funzione multipla per simulare un sovraccarico.

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

In bocca al lupo!


24
Per l'amor di Dio! Usa un'istruzione switch!
jedmao,

8
Inoltre, se insisti a non utilizzare uno switch, dovresti chiamare typeof solo una volta. var type = typeof param; if (type === 'string') ...
Walter Stabosz,

+1 per commentare per "===". L'altro vantaggio dell'istruzione switch è se (... == ...) è sicuro per i tipi.
Nathan Cooper,

4

INTRODUZIONE

Finora la lettura di così tante risposte darebbe a chiunque mal di testa. Chiunque cerchi di conoscere il concetto dovrebbe conoscere i seguenti prerequisiti .

Function overloading Definition, Function Length property,Function argument property

Function overloadingnella sua forma più semplice significa che una funzione esegue compiti diversi sulla base del numero di argomenti che le vengono passati. In particolare, TASK1, TASK2 e TASK3 sono evidenziati di seguito e vengono eseguiti sulla base del numero di argumentspassaggi alla stessa funzione 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 non fornisce la capacità integrata di sovraccarico della funzione.

Alternativa

John E Resig (creatore di JS) ha sottolineato un'alternativa che utilizza i prerequisiti di cui sopra per ottenere la capacità di implementare il sovraccarico delle funzioni.

Il codice seguente utilizza un approccio semplice ma ingenuo utilizzando if-elseo switchistruzione.

  • valuta la argument-lengthproprietà.
  • valori diversi comportano l'invocazione di funzioni diverse.

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

Un'altra tecnica è molto più pulita e dinamica. Il clou di questa tecnica è la addMethodfunzione generica.

  • definiamo una funzione addMethodche viene utilizzata per aggiungere diverse funzioni a un oggetto con lo stesso nome ma con funzionalità diverse .

  • sotto la addMethodfunzione accetta tre parametri nome oggetto object, nome funzione namee la funzione che vogliamo essere invocata fn.

  • La addMethoddefinizione interna var oldmemorizza il riferimento al precedente che functionviene memorizzato con l'aiuto della chiusura: una bolla protettiva.

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

  • utilizzare il debugger per comprendere il flusso del codice.
  • di seguito vengono addMethodaggiunte tre funzioni che, se invocate, utilizzano ninja.whatever(x)con il numero di argomenti xche possono essere qualsiasi cosa, ovvero vuote o una o più di una, invocano funzioni diverse come definite durante l'utilizzo della addMethodfunzione.

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


4

Un altro modo per affrontarlo è utilizzare la variabile speciale: argomenti , questa è un'implementazione:

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

così puoi modificare questo codice in:

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

3

controllalo. È molto bello http://ejohn.org/blog/javascript-method-overloading/ Trick Javascript per consentire di effettuare chiamate in questo modo:

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

Ciao Jaider, controlla la mia risposta, contiene il codice per l' overload del metodo javascript effettivo . Sto parlando Func(new Point())ed Func(new Rectangle())eseguirò diverse funzioni. Ma devo sottolineare che questo è un trucco sporco, poiché il sovraccarico del metodo è in realtà un'attività di compilazione non di runtime.
Keldon Alleyne,

3

Sovraccarico della funzione in Javascript:

Il sovraccarico delle funzioni è la capacità di un linguaggio di programmazione di creare più funzioni con lo stesso nome con implementazioni diverse. quando viene chiamata una funzione sovraccaricata, verrà eseguita una specifica implementazione di quella funzione appropriata al contesto della chiamata. Questo contesto è in genere la quantità di argomenti ricevuti e consente a una chiamata di funzione di comportarsi diversamente a seconda del contesto.

Javascript non ha un sovraccarico della funzione integrata. Tuttavia, questo comportamento può essere emulato in molti modi. Eccone una comoda e semplice:

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

sayHi('Frank', 'Willem');

In scenari in cui non sai quanti argomenti otterrai, puoi usare l' operatore di riposo che è di tre punti .... Convertirà il resto degli argomenti in una matrice. Attenzione però alla compatibilità del browser. Ecco un esempio:

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

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


2

Dato che questo post contiene già molte soluzioni diverse, ho pensato di postarne un altro.

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

}

questo può essere usato come mostrato di seguito:

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

Questa soluzione non è perfetta, ma voglio solo dimostrare come potrebbe essere fatto.


2

Puoi utilizzare "addMethod" da John Resig. Con questo metodo è possibile "sovraccaricare" metodi basati sul conteggio degli argomenti.

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

Ho anche creato un'alternativa a questo metodo che utilizza la memorizzazione nella cache per contenere le variazioni della funzione. Le differenze sono descritte qui

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

2

Forwarding Pattern => la migliore pratica sul sovraccarico di JS

Inoltra a un'altra funzione il cui nome è creato dal 3 ° e 4 ° punto:

  1. Utilizzando il numero di argomenti
  2. Verifica dei tipi di argomenti
window['foo_'+arguments.length+'_'+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments)

Applicazione sul tuo 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(){

  }

Altro campione complesso:

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

2

Funzione Sovraccarico tramite polimorfismo dinamico in 100 linee di JS

Questo è da un insieme più grande di codice che comprende i isFn, isArrecc tipo funzioni di controllo. La versione VanillaJS di seguito è stata rielaborata per rimuovere tutte le dipendenze esterne, tuttavia dovrai definire le tue funzioni di controllo del tipo da utilizzare nelle .add()chiamate.

Nota: questa è una funzione di auto-esecuzione (quindi possiamo avere un ambito di chiusura / ambito chiuso), quindi l'assegnazione a window.overloadpiuttosto che function overload() {...}.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return fnOverload;
    } //# overload

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

    return overload;
}();

Uso:

Il chiamante definisce le funzioni sovraccaricate assegnando una variabile al ritorno di overload(). Grazie al concatenamento, i sovraccarichi aggiuntivi possono essere definiti in 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")
;

L'unico argomento facoltativo per overload()definire la funzione "predefinita" da chiamare se non è possibile identificare la firma. Gli argomenti .add()sono:

  1. fn: functiondefinizione del sovraccarico;
  2. a_vArgumentTests: Arraydi functiondefinire i test da eseguire sul arguments. Ciascuno functionaccetta un singolo argomento e restituisce il truetuo basato su se l'argomento è valido;
  3. sAlias(Opzionale): stringdefinire l'alias per accedere direttamente alla funzione di sovraccarico ( fn), ad esempio myOverloadedFn.noArgs()chiamerà direttamente quella funzione, evitando i test di polimorfismo dinamico degli argomenti.

Questa implementazione in realtà consente oltre ai tradizionali sovraccarichi di funzioni poiché il secondo a_vArgumentTestsargomento .add()in pratica definisce i tipi personalizzati. Quindi, potresti trasferire argomenti non solo in base al tipo, ma a intervalli, valori o raccolte di valori!

Se guardi attraverso le 145 righe di codice overload(), vedrai che ogni firma è classificata in base al numero di argumentspassati ad essa. Questo viene fatto in modo da limitare il numero di test che stiamo eseguendo. Tengo anche traccia del conteggio delle chiamate. Con un po 'di codice aggiuntivo, gli array di funzioni sovraccaricate potrebbero essere riordinati in modo che i test più comunemente chiamati vengano testati per primi, aggiungendo di nuovo un po' di miglioramento delle prestazioni.

Ora, ci sono alcuni avvertimenti ... Dato che Javascript è scritto in modo approssimativo, dovrai stare attento con il tuo vArgumentTestscome integerpotrebbe essere convalidato come un float, ecc.

Versione JSCompress.com (1114 byte, 744 byte con zip g):

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

2

Ora puoi eseguire il sovraccarico delle funzioni in ECMAScript 2018 senza polifillamenti, controllando la lunghezza / tipo var, ecc., Basta usare la sintassi di diffusione .

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"

Che cos'è la sintassi diffusa?

La proposta Rest / Spread Properties for ECMAScript (stage 4) aggiunge le proprietà spread ai letterali degli oggetti. Copia le proprie proprietà enumerabili da un oggetto fornito su un nuovo oggetto. Altro su mdn

Nota: la sintassi diffusa nei letterali degli oggetti non funziona in Edge e IE ed è una funzione sperimentale. vedi compatibilità del browser


2

Qualcosa del genere può essere fatto per sovraccarico di funzioni.

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

fonte


1

Questa è una vecchia domanda ma penso che abbia bisogno di un'altra voce (anche se dubito che qualcuno la leggerà). L'uso delle espressioni di funzione immediatamente invocate (IIFE) può essere utilizzato in combinazione con chiusure e funzioni incorporate per consentire il sovraccarico delle funzioni. Considera il seguente esempio (inventato):

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

In breve, l'uso dell'IIFE crea un ambito locale, permettendoci di definire la variabile privata oldper memorizzare un riferimento alla definizione iniziale della funzione foo. Questa funzione restituisce quindi una funzione incorporata newFooche registra il contenuto di entrambi i due argomenti se viene passato esattamente due argomenti ae bo chiama la oldfunzione if arguments.length !== 2. Questo modello può essere ripetuto un numero qualsiasi di volte per dotare una variabile di diverse difese funzionali.


1

Vorrei condividere un utile esempio di approccio simile a un sovraccarico.

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

Utilizzo: Clear (); // Cancella tutto il documento

Cancella (myDiv); // Cancella il pannello a cui fa riferimento myDiv


1

JavaScript è un linguaggio non tipizzato e penso solo che abbia senso sovraccaricare un metodo / funzione per quanto riguarda il numero di parametri. Pertanto, consiglierei di verificare se il parametro è stato definito:

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

1
Voglio solo notare che non tipizzato non significa "nessun tipo".
hamdiakoguz,

1

A partire da luglio 2017, la seguente è stata la tecnica comune. Si noti che possiamo anche eseguire il controllo del tipo all'interno della funzione.

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

1

Per il tuo caso d'uso, è così che vorrei affrontarlo ES6(poiché è già alla fine del 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;
}

Puoi ovviamente adattarlo per lavorare con qualsiasi quantità di parametri e modificare le tue istruzioni condizionali di conseguenza.


1

non esiste un sovraccarico effettivo in JS, comunque possiamo ancora simulare il sovraccarico del metodo in diversi modi:

metodo n. 1: usa l'oggetto

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

metodo n. 2: utilizzare i parametri di riposo (diffusione)

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

metodo n. 3: utilizzare undefined

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

metodo n. 4: controllo del tipo

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

1

Sebbene i parametri predefiniti non sovraccarichino, potrebbe risolvere alcuni dei problemi che gli sviluppatori devono affrontare in quest'area. L'ingresso è rigorosamente deciso dall'ordine, non è possibile riordinare come si desidera come nel sovraccarico classico:

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

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

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

Risultati in (per l'anno 2020):

2020
16160
12
65
2021
2023

Ulteriori informazioni: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters


0

La prima opzione merita davvero attenzione perché è la cosa che mi è venuta in mente in una configurazione del codice piuttosto complessa. Quindi, la mia risposta è

  1. Utilizzando nomi diversi in primo luogo

Con un piccolo ma essenziale suggerimento, i nomi dovrebbero apparire diversi per il computer, ma non per te. Denominare funzioni sovraccaricate come: func, func1, func2.


Stavo per provare a sovraccaricare, ma ho deciso di usare solo nomi diversi, ad esempio getDeviceInfoByID e getDeviceInfoByType ...
CramerTV
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.