Hay varios aspectos de la sobrecarga de argumentos en Javascript:
Argumentos variables : puede pasar diferentes conjuntos de argumentos (tanto en tipo como en cantidad) y la función se comportará de una manera que coincida con los argumentos que se le pasen.
Argumentos predeterminados : puede definir un valor predeterminado para un argumento si no se pasa.
Argumentos con nombre : el orden de los argumentos se vuelve irrelevante y usted simplemente nombra los argumentos que desea pasar a la función.
A continuación se muestra una sección sobre cada una de estas categorías de manejo de argumentos.
Argumentos variables
Debido a que javascript no tiene verificación de tipo en los argumentos o la cantidad requerida de argumentos, solo puede tener una implementación myFunc()que se adapte a los argumentos que se le pasaron al verificar el tipo, la presencia o la cantidad de argumentos.
jQuery hace esto todo el tiempo. Puede hacer que algunos de los argumentos sean opcionales o puede ramificar su función dependiendo de qué argumentos se le pasen.
Al implementar estos tipos de sobrecargas, tiene varias técnicas diferentes que puede utilizar:
- Puede comprobar la presencia de cualquier argumento determinado comprobando si el valor del nombre del argumento declarado es
undefined.
- Puede verificar la cantidad total o los argumentos con
arguments.length.
- Puede verificar el tipo de cualquier argumento dado.
- Para números variables de argumentos, puede usar la
argumentspseudoarreglo para acceder a cualquier argumento dado con arguments[i].
Aquí hay unos ejemplos:
Veamos el obj.data()método de jQuery . Admite cuatro formas diferentes de uso:
obj.data("key");
obj.data("key", value);
obj.data();
obj.data(object);
Cada uno desencadena un comportamiento diferente y, sin usar esta forma dinámica de sobrecarga, requeriría cuatro funciones separadas.
Así es como uno puede discernir entre todas estas opciones en inglés y luego las combinaré todas en código:
// get the data element associated with a particular key value
obj.data("key");
Si el primer argumento que se pasa a .data()es una cadena y el segundo argumento es undefined, entonces la persona que llama debe utilizar este formulario.
// set the value associated with a particular key
obj.data("key", value);
Si el segundo argumento no está indefinido, establezca el valor de una clave en particular.
// get all keys/values
obj.data();
Si no se pasan argumentos, devuelva todas las claves / valores en un objeto devuelto.
// set all keys/values from the passed in object
obj.data(object);
Si el tipo del primer argumento es un objeto simple, establezca todas las claves / valores de ese objeto.
Así es como puede combinar todos ellos en un conjunto de lógica de 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 clave de esta técnica es asegurarse de que todas las formas de argumentos que desea aceptar sean identificables de forma única y que nunca haya confusión sobre la forma que utiliza la persona que llama. Esto generalmente requiere ordenar los argumentos de manera adecuada y asegurarse de que haya suficiente singularidad en el tipo y la posición de los argumentos para que siempre pueda saber qué forma se está utilizando.
Por ejemplo, si tiene una función que toma tres argumentos de cadena:
obj.query("firstArg", "secondArg", "thirdArg");
Puede hacer que el tercer argumento sea opcional y puede detectar fácilmente esa condición, pero no puede hacer que solo el segundo argumento sea opcional porque no puede decir cuál de estos quiere pasar la persona que llama porque no hay forma de identificar si el segundo El argumento está destinado a ser el segundo argumento o el segundo argumento se omitió, por lo que lo que está en el lugar del segundo argumento es en realidad el tercer argumento:
obj.query("firstArg", "secondArg");
obj.query("firstArg", "thirdArg");
Dado que los tres argumentos son del mismo tipo, no puede distinguir la diferencia entre los diferentes argumentos, por lo que no sabe lo que pretendía la persona que llama. Con este estilo de llamada, solo el tercer argumento puede ser opcional. Si quisiera omitir el segundo argumento, tendría que pasarlo como null(o algún otro valor detectable) en su lugar y su código detectaría eso:
obj.query("firstArg", null, "thirdArg");
Aquí hay un ejemplo de jQuery de argumentos opcionales. Ambos argumentos son opcionales y toman valores predeterminados si no se pasan:
clone: function( dataAndEvents, deepDataAndEvents ) {
dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
return this.map( function () {
return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
});
},
Aquí hay un ejemplo de jQuery en el que puede faltar el argumento o cualquiera de los tres tipos diferentes, lo que le brinda cuatro sobrecargas diferentes:
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;
},
Argumentos nombrados
Otros lenguajes (como Python) permiten pasar argumentos con nombre como un medio para pasar solo algunos argumentos y hacer que los argumentos sean independientes del orden en que se pasan. Javascript no admite directamente la función de argumentos con nombre. Un patrón de diseño que se usa comúnmente en su lugar es pasar un mapa de propiedades / valores. Esto se puede hacer pasando un objeto con propiedades y valores o en ES6 y superior, podría pasar un objeto Map en sí.
Aquí hay un ejemplo simple de ES5:
jQuery's $.ajax()acepta una forma de uso en la que simplemente le pasa un único parámetro que es un objeto Javascript normal con propiedades y valores. Las propiedades que le pase determinan qué argumentos / opciones se pasan a la llamada ajax. Algunos pueden ser necesarios, muchos son opcionales. Dado que son propiedades de un objeto, no existe un orden específico. De hecho, hay más de 30 propiedades diferentes que se pueden pasar a ese objeto, solo se requiere una (la URL).
He aquí un ejemplo:
$.ajax({url: "http://www.example.com/somepath", data: myArgs, dataType: "json"}).then(function(result) {
// process result here
});
Dentro de la $.ajax()implementación, puede simplemente interrogar qué propiedades se pasaron en el objeto entrante y usarlas como argumentos con nombre. Esto se puede hacer con for (prop in obj)o incorporando todas las propiedades en una matriz Object.keys(obj)y luego iterando esa matriz.
Esta técnica se usa muy comúnmente en Javascript cuando hay una gran cantidad de argumentos y / o muchos argumentos son opcionales. Nota: esto impone la responsabilidad de la función de implementación para asegurarse de que haya un conjunto mínimo de argumentos válidos y para darle a la persona que llama algunos comentarios de depuración sobre lo que falta si se pasan argumentos insuficientes (probablemente lanzando una excepción con un mensaje de error útil) .
En un entorno ES6, es posible utilizar la desestructuración para crear propiedades / valores predeterminados para el objeto pasado anteriormente. Esto se discute con más detalle en este artículo de referencia .
Aquí hay un ejemplo de ese artículo:
function selectEntries({ start=0, end=-1, step=1 } = {}) {
···
};
Esto crea propiedades y valores predeterminados para las propiedades start, endy stepen un objeto pasado a la selectEntries()función.
Valores predeterminados para argumentos de función
En ES6, Javascript agrega soporte de lenguaje integrado para valores predeterminados para argumentos.
Por ejemplo:
function multiply(a, b = 1) {
return a*b;
}
multiply(5); // 5
Descripción adicional de las formas en que esto se puede utilizar aquí en MDN .