Существует ли какой-либо неэвариантный способ создания функции с определением времени выполнения?
Есть ли способ создать функцию с реальным именем, определенным во время выполнения без использования eval
, и используя только чистый JavaScript? (Таким образом, нет сгенерированных элементов script
, поскольку они специфичны для среды браузера [и во многих отношениях были бы eval
в масках в любом случае], без использования нестандартных функций одного конкретного механизма JavaScript и т.д.)
Обратите внимание, что я специально не спрашиваю об анонимных функциях, на которые ссылаются переменные или свойства с именами, например:
// NOT this
var name = /* ...come up with the name... */;
var obj = {};
obj[name] = function() { /* ... */ };
Там, когда свойство объекта имеет имя, функция не работает. Анонимные функции хороши для многих вещей, но не то, что я ищу здесь. Я хочу, чтобы функция имела имя (например, отображалась в столах вызовов в отладчиках и т.д.).
Ответы
Ответ 1
Ответ для ECMAScript 2015+ (он же "ES6"):
Да. Начиная с ES2015, функция, созданная выражением анонимной функции, назначенным свойству объекта, получает имя этого свойства объекта. Это реализовано во всех современных браузерах, хотя Edge и Safari не используют имя в трассировке стека. Мы можем использовать это в сочетании с другой функцией ES2015 (имена вычисляемых свойств), чтобы называть функции без new Function
или eval
.
В ES2015 это создает функцию с именем "foo ###", где ### имеет 1-3 цифры:
const dynamicName = "foo" + Math.floor(Math.random() * 1000);
const obj = {
[dynamicName]() {
throw new Error();
}
};
const f = obj[dynamicName];
// See its 'name' property
console.log("Function 'name' property: " + f.name + " (see compatibility note)");
// We can see whether it has a name in stack traces via an exception
try {
f();
} catch (e) {
console.log(e.stack);
}
Ответ 2
Вот функция полезности, с которой я пришел некоторое время назад. Он использует конструктор Function
, как указано в замечании @T.J.Crowder, но улучшает его недостатки и позволяет мелкозернистый контроль над областью действия новой функции.
function NamedFunction(name, args, body, scope, values) {
if (typeof args == "string")
values = scope, scope = body, body = args, args = [];
if (!Array.isArray(scope) || !Array.isArray(values)) {
if (typeof scope == "object") {
var keys = Object.keys(scope);
values = keys.map(function(p) { return scope[p]; });
scope = keys;
} else {
values = [];
scope = [];
}
}
return Function(scope, "function "+name+"("+args.join(", ")+") {\n"+body+"\n}\nreturn "+name+";").apply(null, values);
};
Это позволяет вам быть аккуратным и избегать полного доступа к вашей области с помощью eval
, например. в приведенном выше сценарии:
var f = NamedFunction("fancyname", ["hi"], "display(hi);", {display:display});
f.toString(); // "function fancyname(hi) {
// display(hi);
// }"
f("Hi");