logo

Динамическое получение имён и значений параметров в функции

Быстрый ответ

Извлечение имен параметров и их значений в JavaScript теперь возможно с элегантной простотой:

JS
Скопировать код
const getParams = (func) => {
  const names = func.toString().match(/(?<=\().*?(?=\))/)[0].split(',').map(name => name.trim());
  return (args) => names.reduce((obj, name, index) => (obj[name] = args[index], obj), {});
};

// Практический пример:
function foo(x, y, z) {
  const params = getParams(foo)(arguments);
  console.log(params); // Вывод: { x: 1, y: 2, z: 3 }
}

foo(1, 2, 3);

Этот элегантный метод использует .match() для извлечения параметров из определения функции. Далее с помощью .split и .trim(), он преобразует их в массив имен. С использованием .reduce() свойства и их значения связываются вместе, трансформируя параметры в объект.

Обработка параметров по умолчанию и исключений

Действительность зачастую сложнее, что в полной мере относится и к параметрам функции. В ES6 были введены параметры по умолчанию, которые могут усложнить задачу. Но нет ничего невозможного:

JS
Скопировать код
const getParamsDefault = (func) => {
  const functionString = func.toString();
  const names = functionString.slice(functionString.indexOf('(') + 1, functionString.indexOf(')'))
                              .replace(/\/\*.*?\*\/|\/\/.*(?=\n)/g, '')
                              .split(',')
                              .map(name => name.replace(/=[^,]+/, '').trim());

  return (args) => Array.from(args).reduce((obj, value, index) => {
    obj[names[index]] = value;
    return obj;
  }, {});
};

// Пример использования:
function bar(x, y = 2, z = 3) {
  const params = getParamsDefault(bar)(arguments);
  console.log(params); // Вывод: { x: 1, y: 2, z: 3 }
}

bar(1);

Усовершенствованное регулярное выражение исключает комментарии и значения по умолчанию, позволяя более точно определить имена параметров. Преобразование arguments в массив с помощью Array.from() обеспечивает точное соответствие между именами и значениями параметров.

Использование внешних библиотек для работы с сложностями

Нередко специфические форматы функций или минимизированный код вынуждают нас отказываться от собственных решений в пользу более продвинутых инструментов, таких как AST-парсеры, например, Esprima:

JS
Скопировать код
const esprima = require('esprima');

const getParamsAdvanced = (func) => {
  const ast = esprima.parseScript(func.toString());
  const params = ast.body[0].params.map(param => param.name);
  
  return (args) => Array.from(args).reduce((obj, value, index) => (obj[params[index]] = value, obj), {});
};

// Пример использования:
const complexFunction = (a, b) => { /* Сложная и недооцененная */ };
const params = getParamsAdvanced(complexFunction)(['value1', 'value2']);
console.log(params); // Вывод: { a: 'value1', b: 'value2' }

В сложных случаях на помощь приходит Esprima, преобразуя функцию в абстрактное синтаксическое дерево (AST) и обеспечивая доступ к именам параметров, не обращая внимания на пробелы, комментарии или сокращенную запись кода.

Визуализация

Cчитайте функцию списком ингредиентов для рецепта 🍲. Параметры – это ваши ингредиенты:

Markdown
Скопировать код
function bakeCake(flour, sugar, eggs) { ... }

Мы подготавливаем ингредиенты (параметры) до начала процесса приготовления:

Markdown
Скопировать код
Список ингредиентов: [🌾 мука, 🍚 сахар, 🥚 яйца]

Определение наименования и измерение величины:

JS
Скопировать код
Ингредиенты: ['мука', 'сахар', 'яйца']    // Названия параметров
Количества: [300, 200, 3]                  // Значения параметров

Всё готово, и вы уже способны представить будущее блюдо, взглянув на список ингредиентов.

Markdown
Скопировать код
| Ингредиент | Количество |
| -----------| ---------- |
| 🌾 мука     | 300г       |
| 🍚 сахар    | 200г       |
| 🥚 яйца     | 3 шт.      |

🍲 Осталось только надеть шеф-поварскую шапку и приступить к приготовлению!

Специфика работы с динамическими функциями

Адаптация к различным условиям использования функций требует индивидуального подхода. Будьте особенно осторожны при работе со стрелочными функциями, методами и функциями с синтаксисом остаточных параметров:

  • В стрелочных функциях нет места для arguments. Вместо этого используйте именованные параметры или ...rest.

  • Методы объектов и классов имеют свой контекст (this). Не нарушайте его при работе с параметрами.

  • Остаточные параметры (...args) могут включать все аргументы функции. Учитывайте это при анализе исходного кода функции.

Производительность и лучшие практики

При решении задач, связанных с производительностью, анализ функций видится как лишняя нагрузка. Он замедляет работу и может ввести в заблуждение оптимизирующие алгоритмы JavaScript-движков.

Кроме того, использование регулярных выражений или анализа AST может вызвать заблуждения из-за того, что сторонний код может отличаться от вашего стиля программирования.

В AngularJS практически всегда предпочтительнее использовать существующее внедрение зависимости, например, $inject, чем создавать функцию с нуля.

Полезные материалы

  1. Функции – JavaScript | MDN – ваш надежный гид по параметрам функций в JavaScript.
  2. Reflect – JavaScript | MDN – исследуйте мир метапрограммирования и тонкости работы с Reflect API.
  3. Понимание вызова функции в JavaScript и "this" – углубленное объяснение механизмов вызова функций и контекста this.
  4. Функции высшего порядка :: Eloquent JavaScript – научитесь адаптировать функции под свои конкретные потребности.
  5. Объект arguments – JavaScript | MDN – подробное изучение работы с параметрами функций через объект arguments.