ES2015 e versioni successive
In ES2015, la destrutturazione dei parametri può essere utilizzata per simulare parametri denominati. Richiederebbe al chiamante di passare un oggetto, ma è possibile evitare tutti i controlli all'interno della funzione se si utilizzano anche i parametri predefiniti:
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
Esiste un modo per avvicinarsi a ciò che si desidera, ma si basa sull'output di Function.prototype.toString
[ES5] , che dipende in una certa misura dall'implementazione, quindi potrebbe non essere compatibile con più browser.
L'idea è di analizzare i nomi dei parametri dalla rappresentazione di stringa della funzione in modo da poter associare le proprietà di un oggetto al parametro corrispondente.
Potrebbe quindi apparire una chiamata di funzione
func(a, b, {someArg: ..., someOtherArg: ...});
dove a
e b
sono argomenti posizionali e l'ultimo argomento è un oggetto con argomenti denominati.
Per esempio:
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);
};
};
}());
Che useresti come:
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
DEMO
Ci sono alcuni svantaggi di questo approccio (sei stato avvertito!):
- Se l'ultimo argomento è un oggetto, viene trattato come un "oggetto argomento denominato"
- Otterrai sempre quanti argomenti hai definito nella funzione, ma alcuni di essi potrebbero avere il valore
undefined
(che è diverso dal non avere alcun valore). Ciò significa che non è possibile utilizzare arguments.length
per verificare quanti argomenti sono stati passati.
Invece di avere una funzione che crea il wrapper, potresti anche avere una funzione che accetta una funzione e vari valori come argomenti, come
call(func, a, b, {posArg: ... });
o addirittura estenderti in Function.prototype
modo da poter fare:
foo.execute(a, b, {posArg: ...});