Получить значение по умолчанию функции?
Есть ли способ получить значение аргумента функции по умолчанию в JavaScript?
function foo(x = 5) {
// things I do not control
}
Есть ли способ получить значение по умолчанию x
здесь? Оптимально, что-то вроде:
getDefaultValues(foo); // {"x": 5}
Обратите внимание, что toString
функция не будет работать, так как она будет прерывать значения по умолчанию, которые не являются константами.
Ответы
Ответ 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()
. И вы не хотите полагаться на это обычно.