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: ...});