Подтвердить что ты не робот

Получить значение по умолчанию функции?

Есть ли способ получить значение аргумента функции по умолчанию в JavaScript?

function foo(x = 5) {
    // things I do not control
}

Есть ли способ получить значение по умолчанию x здесь? Оптимально, что-то вроде:

getDefaultValues(foo); // {"x": 5}

Обратите внимание, что toString функция не будет работать, так как она будет прерывать значения по умолчанию, которые не являются константами.

4b9b3361

Ответ 1

Поскольку у нас нет классического отражения в JS, как вы можете найти на С#, Ruby и т.д., мы должны полагаться на один из моих любимых инструментов, регулярных выражений, чтобы выполнить эту работу для нас:

let b = "foo";
function fn (x = 10, /* woah */ y = 20, z, a = b) { /* ... */ }

fn.toString()
  .match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1] // Get the parameters declaration between the parenthesis
  .replace(/(\/\*[\s\S]*?\*\/)/mg,'')             // Get rid of comments
  .split(',')
  .reduce(function (parameters, param) {          // Convert it into an object
    param = param.match(/([_$a-zA-Z][^=]*)(?:=([^=]+))?/); // Split parameter name from value
    parameters[param[1].trim()] = eval(param[2]); // Eval each default value, to get strings, variable refs, etc.

    return parameters;
  }, {});

// Object { x: 10, y: 20, z: undefined, a: "foo" }

Если вы собираетесь использовать это, просто убедитесь, что вы кэшируете регулярные выражения для производительности.

Благодаря bubersson для подсказок для первых двух регулярных выражений

Ответ 2

Я бы справился с этим, извлекая параметры из строковой версии функции:

// making x=3 into {x: 3}
function serialize(args) {
  var obj = {};
  var noWhiteSpace = new RegExp(" ", "g");
  args = args.split(",");
  args.forEach(function(arg) {
    arg = arg.split("=");
    var key = arg[0].replace(noWhiteSpace, "");
    obj[key] = arg[1];
  });
  return obj;
  }

 function foo(x=5, y=7, z='foo') {}

// converting the function into a string
var fn = foo.toString();

// magic regex to extract the arguments 
var args = /\(\s*([^)]+?)\s*\)/.exec(fn);

//getting the object
var argMap = serialize(args[1]); //  {x: "5", y: "7", z: "'foo'"}

был извлечен метод извлечения аргументов: Регулярное выражение для получения списка параметров из определения функции

ура!

PS. как вы можете видеть, он отличает целые числа в строки, что иногда может раздражать. просто убедитесь, что вы знаете тип ввода заранее или убедитесь, что это не имеет значения.

Ответ 3

Как говорится в вопросе, использование toString является ограниченным решением. Это даст результат, который может быть чем угодно: от литерала значения до вызова метода. Однако это касается самого языка - он допускает такие объявления. Преобразование всего, что не является буквальным значением для значения, является эвристическим предположением в лучшем случае. Рассмотрим следующие 2 фрагмента кода:

let def;
function withDef(v = def) {
  console.log(v);
}

getDefaultValues(withDef); // undefined, or unknown?

def = prompt('Default value');
withDef();
function wrap() {
  return (new Function(prompt('Function body')))();
  // mutate some other value(s) --> side effects
}

function withDef(v = wrap()) {
  console.log(v);
}
withDef();
getDefaultValues(withDef); // unknown?

В то время как первый пример может быть оценен (рекурсивно при необходимости) для извлечения undefined, а затем для любого другого значения, второй действительно undefined, поскольку значение по умолчанию является недетерминированным. Конечно, вы можете заменить prompt() на любой другой внешний вход/случайный генератор.

Итак, лучший ответ - тот, который у вас уже есть. Используйте toString и, если хотите, eval() то, что вы извлекаете, но оно будет иметь побочные эффекты.

Ответ 4

Есть ли способ получить значение по умолчанию x здесь?

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

Обратите внимание, что toString() функция не будет работать, так как она будет прерывать значения по умолчанию, которые не являются константами.

Действительно. Ваш единственный способ узнать это - вызвать функцию, поскольку значения по умолчанию могут зависеть от вызова. Просто подумайте о

function(x, y=x) { … }

и попытайтесь получить разумное представление для значения y по умолчанию.

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

Напротив, JS действительно оценивает выражения инициализатора параметра при каждом вызове функции (если требуется) - посмотрите Как работает this по умолчанию? для деталей. И эти выражения, как если бы они были частью тела функций, не были доступны программно, так же как любые значения, на которые они ссылаются, скрыты в закрытом закрытии функции. Если у вас есть API отражения, который позволяет получить доступ к значениям закрытия (например, API отладки двигателя), вы также можете получить доступ к значениям по умолчанию.

Совершенно невозможно отличить function(x, y=x) {…} от function(x) { var y=x; …} от их общедоступных свойств или от их поведения, единственный способ - .toString(). И вы не хотите полагаться на это обычно.