Ci sono molti aspetti del sovraccarico di argomenti in Javascript:
Argomenti variabili : è possibile passare diversi set di argomenti (sia per tipo che per quantità) e la funzione si comporterà in un modo che corrisponda agli argomenti passati.
Argomenti predefiniti : è possibile definire un valore predefinito per un argomento se non viene passato.
Argomenti con nome : l'ordine degli argomenti diventa irrilevante e devi solo nominare gli argomenti che vuoi passare alla funzione.
Di seguito è riportata una sezione su ciascuna di queste categorie di gestione degli argomenti.
Argomenti variabili
Poiché javascript non ha il controllo del tipo sugli argomenti o la quantità richiesta di argomenti, puoi avere solo un'implementazione di myFunc()che può adattarsi a quali argomenti sono stati passati controllando il tipo, la presenza o la quantità di argomenti.
jQuery lo fa tutto il tempo. Puoi rendere alcuni degli argomenti opzionali o puoi ramificare la tua funzione a seconda di quali argomenti vengono passati.
Nell'implementazione di questi tipi di sovraccarichi, sono disponibili diverse tecniche che è possibile utilizzare:
- È possibile verificare la presenza di un determinato argomento controllando se il valore del nome dell'argomento dichiarato è
undefined.
- Puoi controllare la quantità totale o gli argomenti con
arguments.length.
- Puoi controllare il tipo di un dato argomento.
- Per numeri variabili di argomenti, è possibile utilizzare lo
argumentspseudo-array per accedere a qualsiasi argomento dato con arguments[i].
Ecco alcuni esempi:
Diamo un'occhiata al obj.data()metodo di jQuery . Supporta quattro diverse forme di utilizzo:
obj.data("key");
obj.data("key", value);
obj.data();
obj.data(object);
Ognuno innesca un comportamento diverso e, senza utilizzare questa forma dinamica di sovraccarico, richiederebbe quattro funzioni separate.
Ecco come si possono discernere tutte queste opzioni in inglese e poi le combinerò tutte in codice:
// get the data element associated with a particular key value
obj.data("key");
Se il primo argomento passato a .data()è una stringa e il secondo argomento lo è undefined, il chiamante deve utilizzare questo modulo.
// set the value associated with a particular key
obj.data("key", value);
Se il secondo argomento non è indefinito, imposta il valore di una particolare chiave.
// get all keys/values
obj.data();
Se non vengono passati argomenti, restituisce tutte le chiavi / valori in un oggetto restituito.
// set all keys/values from the passed in object
obj.data(object);
Se il tipo del primo argomento è un oggetto semplice, imposta tutte le chiavi / valori da quell'oggetto.
Ecco come puoi combinare tutti questi in un set di logica 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
}
},
La chiave di questa tecnica è assicurarsi che tutte le forme di argomenti che si desidera accettare siano identificabili in modo univoco e che non vi sia mai alcuna confusione su quale forma sta utilizzando il chiamante. Ciò richiede generalmente di ordinare gli argomenti in modo appropriato e assicurarsi che ci sia abbastanza unicità nel tipo e nella posizione degli argomenti da poter sempre dire quale forma viene utilizzata.
Ad esempio, se hai una funzione che accetta tre argomenti stringa:
obj.query("firstArg", "secondArg", "thirdArg");
Puoi facilmente rendere facoltativo il terzo argomento e puoi facilmente rilevare quella condizione, ma non puoi rendere facoltativo solo il secondo argomento perché non puoi dire quale di questi il chiamante intende passare perché non c'è modo di identificare se il secondo argomento è pensato per essere il secondo argomento o il secondo argomento è stato omesso, quindi ciò che è al posto del secondo argomento è in realtà il terzo argomento:
obj.query("firstArg", "secondArg");
obj.query("firstArg", "thirdArg");
Poiché tutti e tre gli argomenti sono dello stesso tipo, non puoi distinguere tra argomenti diversi, quindi non sai cosa intendeva il chiamante. Con questo stile di chiamata, solo il terzo argomento può essere facoltativo. Se volessi omettere il secondo argomento, dovrebbe essere passato come null(o qualche altro valore rilevabile) e il tuo codice rileverà che:
obj.query("firstArg", null, "thirdArg");
Ecco un esempio jQuery di argomenti opzionali. entrambi gli argomenti sono opzionali e assumono valori predefiniti se non vengono passati:
clone: function( dataAndEvents, deepDataAndEvents ) {
dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
return this.map( function () {
return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
});
},
Ecco un esempio di jQuery in cui l'argomento può mancare o uno qualsiasi dei tre diversi tipi che ti dà quattro diversi overload:
html: function( value ) {
if ( value === undefined ) {
return this[0] && this[0].nodeType === 1 ?
this[0].innerHTML.replace(rinlinejQuery, "") :
null;
// See if we can take a shortcut and just use innerHTML
} else if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
(jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
!wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
value = value.replace(rxhtmlTag, "<$1></$2>");
try {
for ( var i = 0, l = this.length; i < l; i++ ) {
// Remove element nodes and prevent memory leaks
if ( this[i].nodeType === 1 ) {
jQuery.cleanData( this[i].getElementsByTagName("*") );
this[i].innerHTML = value;
}
}
// If using innerHTML throws an exception, use the fallback method
} catch(e) {
this.empty().append( value );
}
} else if ( jQuery.isFunction( value ) ) {
this.each(function(i){
var self = jQuery( this );
self.html( value.call(this, i, self.html()) );
});
} else {
this.empty().append( value );
}
return this;
},
Argomenti denominati
Altri linguaggi (come Python) consentono di passare argomenti con nome come mezzo per passare solo alcuni argomenti e rendere gli argomenti indipendenti dall'ordine in cui sono passati. Javascript non supporta direttamente la funzionalità degli argomenti con nome. Un modello di progettazione che viene comunemente utilizzato al suo posto è passare una mappa di proprietà / valori. Questo può essere fatto passando un oggetto con proprietà e valori o in ES6 e versioni successive, potresti effettivamente passare un oggetto Map stesso.
Ecco un semplice esempio ES5:
jQuery $.ajax()accetta una forma di utilizzo in cui basta passare un singolo parametro che è un normale oggetto Javascript con proprietà e valori. Le proprietà passate determinano quali argomenti / opzioni vengono passati alla chiamata ajax. Alcuni possono essere richiesti, molti sono opzionali. Poiché sono proprietà su un oggetto, non esiste un ordine specifico. In effetti, ci sono più di 30 proprietà diverse che possono essere trasmesse a quell'oggetto, solo una (l'url) è richiesta.
Ecco un esempio:
$.ajax({url: "http://www.example.com/somepath", data: myArgs, dataType: "json"}).then(function(result) {
// process result here
});
All'interno $.ajax()dell'implementazione, può quindi semplicemente interrogare quali proprietà sono state passate sull'oggetto in arrivo e usarle come argomenti con nome. Questo può essere fatto con for (prop in obj)o inserendo tutte le proprietà in un array con Object.keys(obj)e quindi iterando quell'array.
Questa tecnica è usata molto comunemente in Javascript quando ci sono un gran numero di argomenti e / o molti argomenti sono opzionali. Nota: questo impone alla funzione di implementazione di assicurarsi che sia presente un insieme minimo di argomenti validi e di fornire al chiamante un feedback di debug su cosa manca se vengono passati argomenti insufficienti (probabilmente lanciando un'eccezione con un utile messaggio di errore) .
In un ambiente ES6, è possibile utilizzare la destrutturazione per creare proprietà / valori predefiniti per l'oggetto passato sopra. Questo argomento viene discusso in maggiore dettaglio in questo articolo di riferimento .
Ecco un esempio tratto da quell'articolo:
function selectEntries({ start=0, end=-1, step=1 } = {}) {
···
};
Questo crea le proprietà ei valori predefiniti per il start, ende steple proprietà di un oggetto passato alla selectEntries()funzione di.
Valori predefiniti per gli argomenti della funzione
In ES6, Javascript aggiunge il supporto linguistico integrato per i valori predefiniti per gli argomenti.
Per esempio:
function multiply(a, b = 1) {
return a*b;
}
multiply(5); // 5
Ulteriore descrizione dei modi in cui questo può essere utilizzato qui su MDN .