Il y a plusieurs aspects à la surcharge d'arguments en Javascript:
Arguments variables - Vous pouvez transmettre différents ensembles d'arguments (en type et en quantité) et la fonction se comportera d'une manière qui correspond aux arguments qui lui sont passés.
Arguments par défaut - Vous pouvez définir une valeur par défaut pour un argument s'il n'est pas passé.
Arguments nommés - L'ordre des arguments devient sans importance et vous ne nommez que les arguments que vous souhaitez transmettre à la fonction.
Vous trouverez ci-dessous une section sur chacune de ces catégories de gestion des arguments.
Arguments de variable
Comme javascript n'a pas de vérification de type sur les arguments myFunc()ou de quantité d'arguments requise, vous ne pouvez avoir qu'une seule implémentation de qui peut s'adapter aux arguments qui lui ont été passés en vérifiant le type, la présence ou la quantité d'arguments.
jQuery fait cela tout le temps. Vous pouvez rendre certains des arguments optionnels ou vous pouvez créer une branche dans votre fonction en fonction des arguments qui lui sont passés.
Lors de l'implémentation de ces types de surcharges, vous pouvez utiliser plusieurs techniques différentes:
- Vous pouvez vérifier la présence d'un argument donné en vérifiant si la valeur du nom d'argument déclaré est
undefined.
- Vous pouvez vérifier la quantité totale ou les arguments avec
arguments.length.
- Vous pouvez vérifier le type de tout argument donné.
- Pour des nombres variables d'arguments, vous pouvez utiliser le
argumentspseudo-tableau pour accéder à n'importe quel argument donné avec arguments[i].
Voici quelques exemples:
Regardons la obj.data()méthode de jQuery . Il prend en charge quatre formes d'utilisation différentes:
obj.data("key");
obj.data("key", value);
obj.data();
obj.data(object);
Chacun déclenche un comportement différent et, sans utiliser cette forme dynamique de surcharge, nécessiterait quatre fonctions distinctes.
Voici comment on peut discerner toutes ces options en anglais, puis je les combinerai toutes en code:
// get the data element associated with a particular key value
obj.data("key");
Si le premier argument passé à .data()est une chaîne et le deuxième argument est undefined, l'appelant doit utiliser ce formulaire.
// set the value associated with a particular key
obj.data("key", value);
Si le deuxième argument n'est pas indéfini, définissez la valeur d'une clé particulière.
// get all keys/values
obj.data();
Si aucun argument n'est passé, renvoie toutes les clés / valeurs d'un objet retourné.
// set all keys/values from the passed in object
obj.data(object);
Si le type du premier argument est un objet simple, définissez toutes les clés / valeurs de cet objet.
Voici comment vous pouvez combiner tous ces éléments dans un seul ensemble de logique 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 clé de cette technique est de s'assurer que toutes les formes d'arguments que vous souhaitez accepter sont identifiables de manière unique et qu'il n'y a jamais de confusion sur la forme utilisée par l'appelant. Cela nécessite généralement de classer les arguments de manière appropriée et de s'assurer que le type et la position des arguments sont suffisamment uniques pour que vous puissiez toujours dire quelle forme est utilisée.
Par exemple, si vous avez une fonction qui prend trois arguments de chaîne:
obj.query("firstArg", "secondArg", "thirdArg");
Vous pouvez facilement rendre le troisième argument facultatif et vous pouvez facilement détecter cette condition, mais vous ne pouvez pas rendre seulement le deuxième argument facultatif car vous ne pouvez pas dire lequel de ces arguments l'appelant veut passer car il n'y a aucun moyen d'identifier si le second L'argument est censé être le deuxième argument ou le deuxième argument a été omis, donc ce qui est à la place du deuxième argument est en fait le troisième argument:
obj.query("firstArg", "secondArg");
obj.query("firstArg", "thirdArg");
Étant donné que les trois arguments sont du même type, vous ne pouvez pas faire la différence entre les différents arguments et vous ne savez donc pas ce que l'appelant a voulu. Avec ce style d'appel, seul le troisième argument peut être facultatif. Si vous vouliez omettre le deuxième argument, il devrait être passé comme null(ou une autre valeur détectable) à la place et votre code le détecterait:
obj.query("firstArg", null, "thirdArg");
Voici un exemple jQuery d'arguments optionnels. les deux arguments sont facultatifs et prennent des valeurs par défaut s'ils ne sont pas passés:
clone: function( dataAndEvents, deepDataAndEvents ) {
dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
return this.map( function () {
return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
});
},
Voici un exemple jQuery où l'argument peut être manquant ou l'un des trois types différents qui vous donne quatre surcharges différentes:
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;
},
Arguments nommés
D'autres langages (comme Python) permettent de passer des arguments nommés comme moyen de ne transmettre que quelques arguments et de rendre les arguments indépendants de l'ordre dans lequel ils sont passés. Javascript ne prend pas directement en charge la fonctionnalité des arguments nommés. Un modèle de conception couramment utilisé à sa place consiste à transmettre une carte de propriétés / valeurs. Cela peut être fait en passant un objet avec des propriétés et des valeurs ou dans ES6 et au-dessus, vous pouvez en fait passer un objet Map lui-même.
Voici un exemple simple ES5:
jQuery's $.ajax()accepte une forme d'utilisation où vous ne lui passez qu'un seul paramètre qui est un objet Javascript régulier avec des propriétés et des valeurs. Les propriétés que vous passez déterminent quels arguments / options sont passés à l'appel ajax. Certains peuvent être requis, beaucoup sont facultatifs. Puisqu'il s'agit de propriétés sur un objet, il n'y a pas d'ordre spécifique. En fait, il y a plus de 30 propriétés différentes qui peuvent être transmises à cet objet, une seule (l'url) est requise.
Voici un exemple:
$.ajax({url: "http://www.example.com/somepath", data: myArgs, dataType: "json"}).then(function(result) {
// process result here
});
À l'intérieur de l' $.ajax()implémentation, il peut alors simplement interroger les propriétés qui ont été transmises à l'objet entrant et les utiliser comme arguments nommés. Cela peut être fait avec for (prop in obj)ou en obtenant toutes les propriétés dans un tableau avec Object.keys(obj)puis en itérant ce tableau.
Cette technique est très couramment utilisée en Javascript lorsqu'il y a un grand nombre d'arguments et / ou de nombreux arguments sont optionnels. Remarque: cela impose à la fonction d'implémentation de s'assurer qu'un ensemble minimal d'arguments valides est présent et de donner à l'appelant des commentaires de débogage sur ce qui manque si des arguments insuffisants sont passés (probablement en lançant une exception avec un message d'erreur utile) .
Dans un environnement ES6, il est possible d'utiliser la déstructuration pour créer des propriétés / valeurs par défaut pour l'objet passé ci-dessus. Ceci est discuté plus en détail dans cet article de référence .
Voici un exemple de cet article:
function selectEntries({ start=0, end=-1, step=1 } = {}) {
···
};
Cela crée des propriétés et des valeurs par défaut pour les propriétés start, endet stepsur un objet passé à la selectEntries()fonction.
Valeurs par défaut des arguments de fonction
Dans ES6, Javascript ajoute la prise en charge du langage intégré pour les valeurs par défaut des arguments.
Par exemple:
function multiply(a, b = 1) {
return a*b;
}
multiply(5); // 5
Une description plus détaillée des façons dont cela peut être utilisé ici sur MDN .