La surcharge d'arguments en Javascript présente de multiples aspects :
-
Arguments variables - Vous pouvez passer différents ensembles d'arguments (à la fois en termes de type et de 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 n'a plus d'importance et il suffit de nommer les arguments que l'on veut passer à la fonction.
Vous trouverez ci-dessous une section sur chacune de ces catégories de traitement des arguments.
Arguments variables
Étant donné que le javascript n'a pas de vérification de type sur les arguments ou de quantité d'arguments requise, vous pouvez avoir une seule implémentation de myFunc()
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 facultatifs ou vous pouvez brancher votre fonction en fonction des arguments qui lui sont passés.
Pour mettre en œuvre 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 déclarée du nom de l'argument 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 un nombre variable d'arguments, vous pouvez utiliser la fonction
arguments
pour accéder à n'importe quel argument donné avec arguments[i]
.
Voici quelques exemples :
Examinons la méthode de jQuery. obj.data()
méthode. Il prend en charge quatre formes d'utilisation différentes :
obj.data("key");
obj.data("key", value);
obj.data();
obj.data(object);
Chacune d'entre elles déclenche un comportement différent et, sans l'utilisation de cette forme dynamique de surcharge, elle nécessiterait quatre fonctions distinctes.
Voici comment on peut discerner entre 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 de caractères et le second argument est undefined
alors 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, alors définir la valeur d'une clé particulière.
// get all keys/values
obj.data();
Si aucun argument n'est passé, alors il retourne toutes les clés/valeurs dans 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, alors il faut définir toutes les clés/valeurs de cet objet.
Voici comment vous pourriez combiner tous ces éléments en 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 voulez 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 d'ordonner les arguments de manière appropriée et de s'assurer qu'il y a suffisamment d'unicité dans le type et la position des arguments pour que vous puissiez toujours dire quelle forme est utilisée.
Par exemple, si vous avez une fonction qui prend trois arguments de type chaîne :
obj.query("firstArg", "secondArg", "thirdArg");
Vous pouvez facilement rendre le troisième argument optionnel et vous pouvez facilement détecter cette condition, mais vous ne pouvez pas rendre seulement le deuxième argument optionnel parce que vous ne pouvez pas dire lequel de ces arguments l'appelant veut passer parce qu'il n'y a aucun moyen d'identifier si le deuxième argument est censé être le deuxième argument ou si 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");
Comme 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 voulait. 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é en tant que null
(ou une autre valeur détectable) à la place et votre code le détectera :
obj.query("firstArg", null, "thirdArg");
Voici un exemple jQuery d'arguments optionnels. Les deux arguments sont optionnels 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 absent ou de l'un des trois types différents, ce 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, ce qui permet de ne passer que certains 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 plus, vous pouvez passer un objet Map lui-même.
Voici un exemple simple en ES5 :
La méthode de jQuery $.ajax()
accepte une forme d'utilisation où vous lui passez un seul paramètre qui est un objet Javascript ordinaire avec des propriétés et des valeurs. Les propriétés que vous lui passez déterminent les arguments/options qui sont passés à l'appel ajax. Certains sont obligatoires, beaucoup sont optionnels. Comme il s'agit de propriétés d'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 sur cet objet, une seule (l'url) est obligatoire.
Voici un exemple :
$.ajax({url: "http://www.example.com/somepath", data: myArgs, dataType: "json"}).then(function(result) {
// process result here
});
Intérieur de la $.ajax()
il peut alors simplement interroger les propriétés qui ont été passées sur l'objet entrant et les utiliser comme arguments nommés. Cela peut être fait soit avec for (prop in obj)
ou en récupérant toutes les propriétés dans un tableau avec Object.keys(obj)
et ensuite itérer ce tableau.
Cette technique est utilisée très couramment en Javascript lorsqu'il y a un grand nombre d'arguments et/ou que de nombreux arguments sont facultatifs. Remarque : il incombe à la fonction d'implémentation de s'assurer qu'un ensemble minimal d'arguments valides est présent et de donner à l'appelant des informations 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. Ce point est abordé plus en détail dans cet article de référence .
Voici un exemple tiré de cet article :
function selectEntries({ start=0, end=-1, step=1 } = {}) {
···
};
Ensuite, vous pouvez l'appeler comme n'importe lequel de ceux-ci :
selectEntries({start: 5});
selectEntries({start: 5, end: 10});
selectEntries({start: 5, end: 10, step: 2});
selectEntries({step: 3});
selectEntries();
Les arguments que vous ne mentionnez pas dans l'appel de la fonction prendront leur valeur par défaut dans la déclaration de la fonction.
Cela crée des propriétés et des valeurs par défaut pour le start
, end
y step
sur un objet passé à la fonction selectEntries()
fonction.
Valeurs par défaut des arguments de fonction
Dans l'ES6, Javascript ajoute un support linguistique intégré pour les valeurs par défaut des arguments.
Par exemple :
function multiply(a, b = 1) {
return a*b;
}
multiply(5); // 5
Description plus détaillée des possibilités d'utilisation ici sur MDN .
1 votes
Si vous avez des problèmes avec les surcharges et le champ d'application, vous devriez probablement utiliser un nom de méthode différent, car il semble qu'elle fasse un autre travail.
0 votes
@joshcomley - Les surcharges et la portée nécessitent du code pour les gérer. J'essaie simplement de m'assurer que le code que j'utilise est aussi efficace que possible. Gérer les scopes avec des solutions de contournement, c'est bien, mais je préfère au moins essayer d'utiliser les méthodes des meilleures pratiques.
3 votes
Duplicata possible de Surcharge de fonctions en Javascript - Meilleures pratiques