ES2015 и позже
В ES2015 деструктуризация параметров может использоваться для моделирования именованных параметров. Требуется, чтобы вызывающая сторона передала объект, но вы можете избежать всех проверок внутри функции, если вы также используете параметры по умолчанию:
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
Есть способ приблизиться к тому, что вы хотите, но он основан на выводе Function.prototype.toString
[ES5] , который в некоторой степени зависит от реализации, поэтому он может быть несовместим с браузерами.
Идея состоит в том, чтобы проанализировать имена параметров из строкового представления функции, чтобы можно было связать свойства объекта с соответствующим параметром.
Вызов функции может выглядеть так
func(a, b, {someArg: ..., someOtherArg: ...});
где a
и b
являются позиционными аргументами, а последний аргумент является объектом с именованными аргументами.
Например:
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);
};
};
}());
Который вы бы использовали как:
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
У этого подхода есть некоторые недостатки (вас предупредили!):
- Если последний аргумент является объектом, он обрабатывается как «объекты именованного аргумента»
- Вы всегда получите столько аргументов, сколько вы определили в функции, но некоторые из них могут иметь значение
undefined
(это отличается от отсутствия значения вообще). Это означает, что вы не можете использовать, arguments.length
чтобы проверить, сколько аргументов было передано.
Вместо того, чтобы иметь функцию, создающую оболочку, вы также можете иметь функцию, которая принимает функцию и различные значения в качестве аргументов, такие как
call(func, a, b, {posArg: ... });
или даже расширить, Function.prototype
чтобы вы могли сделать:
foo.execute(a, b, {posArg: ...});