ES2015 y posterior
En ES2015, la desestructuración de parámetros se puede utilizar para simular parámetros con nombre. Requeriría que la persona que llama pasara un objeto, pero puede evitar todas las comprobaciones dentro de la función si también utiliza parámetros predeterminados:
myFunction({ param1 : 70, param2 : 175});
function myFunction({param1, param2}={}){
// ...function body...
}
// Or with defaults,
function myFunc({
name = 'Default user',
age = 'N/A'
}={}) {
// ...function body...
}
ES5
Hay una manera de acercarse a lo que desea, pero se basa en la salida de Function.prototype.toString
[ES5] , que depende en cierta medida de la implementación, por lo que podría no ser compatible con varios navegadores.
La idea es analizar los nombres de los parámetros de la representación de cadena de la función para que pueda asociar las propiedades de un objeto con el parámetro correspondiente.
Una llamada a la función podría verse así
func(a, b, {someArg: ..., someOtherArg: ...});
donde a
y b
son argumentos posicionales y el último argumento es un objeto con argumentos nombrados.
Por ejemplo:
var parameterfy = (function() {
var pattern = /function[^(]*\(([^)]*)\)/;
return function(func) {
// fails horribly for parameterless functions ;)
var args = func.toString().match(pattern)[1].split(/,\s*/);
return function() {
var named_params = arguments[arguments.length - 1];
if (typeof named_params === 'object') {
var params = [].slice.call(arguments, 0, -1);
if (params.length < args.length) {
for (var i = params.length, l = args.length; i < l; i++) {
params.push(named_params[args[i]]);
}
return func.apply(this, params);
}
}
return func.apply(null, arguments);
};
};
}());
Que usarías como:
var foo = parameterfy(function(a, b, c) {
console.log('a is ' + a, ' | b is ' + b, ' | c is ' + c);
});
foo(1, 2, 3); // a is 1 | b is 2 | c is 3
foo(1, {b:2, c:3}); // a is 1 | b is 2 | c is 3
foo(1, {c:3}); // a is 1 | b is undefined | c is 3
foo({a: 1, c:3}); // a is 1 | b is undefined | c is 3
MANIFESTACIÓN
Hay algunos inconvenientes en este enfoque (¡ya te lo advertimos!):
- Si el último argumento es un objeto, se trata como un "objeto de argumento con nombre"
- Siempre obtendrá tantos argumentos como haya definido en la función, pero algunos de ellos pueden tener el valor
undefined
(eso es diferente de no tener ningún valor). Eso significa que no puede usar arguments.length
para probar cuántos argumentos se han pasado.
En lugar de tener una función que cree el contenedor, también podría tener una función que acepte una función y varios valores como argumentos, como
call(func, a, b, {posArg: ... });
o incluso extender Function.prototype
para que puedas hacer:
foo.execute(a, b, {posArg: ...});