У меня есть это:
this.f = function instance(){};
Я хотел бы иметь это:
this.f = function ["instance:" + a](){};
У меня есть это:
this.f = function instance(){};
Я хотел бы иметь это:
this.f = function ["instance:" + a](){};
Как уже упоминалось, это не самое быстрое и рекомендуемое решение. Решение Marcosc ниже - это путь.
Вы можете использовать eval:
var code = "this.f = function " + instance + "() {...}";
eval(code);
Это будет в основном делать это на самом простом уровне:
"use strict";
var name = "foo";
var func = new Function(
"return function " + name + "(){ alert('sweet!')}"
)();
//call it, to test it
func();
Если вы хотите получить больше фантазии, у меня есть статья о " Динамические имена функций в JavaScript".
Вы можете использовать Object.defineProperty, как указано в Справочнике JavaScript MDN [1]:
var myName = "myName";
var f = function () { return true; };
Object.defineProperty(f, 'name', {value: myName, writable: false});
В последних версиях вы можете сделать
function nameFunction(name, body) {
return {[name](...args) {return body(...args)}}[name]
}
const x = nameFunction("wonderful function", (p) => p*2)
console.log(x(9)) // => 18
console.log(x.name) // => "wonderful function"
Я думаю, что большинство предложений здесь субоптимальные, используя eval, hacky solutions или wrappers. Начиная с ES2015 имена выводятся из синтаксической позиции для переменных и свойств.
Так что все будет хорошо:
const name = 'myFn';
const fn = {[name]: function() {}}[name];
fn.name // 'myFn'
Сопротивляйтесь соблазну создавать именованные функции фабричных методов, так как вы не сможете передавать функцию извне и модифицировать ее в синтаксическую позицию, чтобы вывести ее имя. Тогда это уже слишком поздно. Если вам это действительно нужно, вам нужно создать обертку. Кто-то сделал это здесь, но это решение не работает для классов (которые также являются функциями).
Здесь был написан более подробный ответ со всеми описанными вариантами: fooobar.com/questions/116353/...
Синтаксис function[i](){}
подразумевает объект со значениями свойств, которые являются функциями, function[]
, индексированными по имени, [i]
.
Таким образом {"f:1":function(){}, "f:2":function(){}, "f:A":function(){}, ... } ["f:"+i]
.
{"f:1":function f1(){}, "f:2":function f2(){}, "f:A":function fA(){}} ["f:"+i]
сохранит идентификацию имени функции. См. Примечания ниже относительно :
.
Итак,
javascript: alert(
new function(a){
this.f={"instance:1":function(){}, "instance:A":function(){}} ["instance:"+a]
}("A") . toSource()
);
отображает ({f:(function () {})})
в FireFox.
(Это почти та же идея, что и это решение, только он использует общий объект и больше не напрямую заполняет объект окна с помощью функций.)
Этот метод явно заполняет среду с помощью instance:x
.
javascript: alert(
new function(a){
this.f=eval("instance:"+a+"="+function(){})
}("A") . toSource()
);
alert(eval("instance:A"));
отображает
({f:(function () {})})
и
function () {
}
Хотя функция свойства f
ссылается на anonymous function
, а не instance:x
, этот метод позволяет избежать нескольких проблем с этим решением.
javascript: alert(
new function(a){
eval("this.f=function instance"+a+"(){}")
}("A") . toSource()
);
alert(instanceA); /* is undefined outside the object context */
отображается только
({f:(function instanceA() {})})
:
делает javascript function instance:a(){}
недействительным.eval
.Не обязательно проблематично следующее:
instanceA
недоступна для использования как instanceA()
и, следовательно, гораздо более согласуется с исходным контекстом проблемы.
Учитывая эти соображения,
this.f = {"instance:1": function instance1(){},
"instance:2": function instance2(){},
"instance:A": function instanceA(){},
"instance:Z": function instanceZ(){}
} [ "instance:" + a ]
максимально поддерживает глобальную вычислительную среду с семантикой и синтаксисом примера OP.
Самый проголосовавший ответ уже определил тело функции [String]. Я искал решение для переименования уже объявленного имени функции, и, наконец, после часа борьбы я справился с этим. Это:
.toString()
function
и (
new Function()
function nameAppender(name,fun){
const reg = /^(function)(?:\s*|\s+([A-Za-z0-9_$]+)\s*)(\()/;
return (new Function(`return ${fun.toString().replace(reg,`$1 ${name}$3`)}`))();
}
//WORK FOR ALREADY NAMED FUNCTIONS:
function hello(name){
console.log('hello ' + name);
}
//rename the 'hello' function
var greeting = nameAppender('Greeting', hello);
console.log(greeting); //function Greeting(name){...}
//WORK FOR ANONYMOUS FUNCTIONS:
//give the name for the anonymous function
var count = nameAppender('Count',function(x,y){
this.x = x;
this.y = y;
this.area = x*y;
});
console.log(count); //function Count(x,y){...}
Что насчет
this.f = window["instance:" + a] = function(){};
Единственным недостатком является то, что функция в методе toSource не указывает имя. Это обычно только проблема для отладчиков.
Для установки имени существующей анонимной функции:
(На основе ответа @Marcosc)
var anonymous = function() { return true; }
var name = 'someName';
var strFn = anonymous.toString().replace('function ', 'return function ' + name);
var fn = new Function(strFn)();
console.log(fn()); // —> true
Примечание: не делайте этого; /
Динамические методы объекта могут быть созданы с использованием Object Literal Extensions, предоставленных ECMAScript 2015 (ES6):
const postfixes = ['foo', 'bar'];
const mainObj = {};
const makeDynamic = (postfix) => {
const newMethodName = 'instance: ' + postfix;
const tempObj = {
[newMethodName]() {
console.log(`called method ${newMethodName}`);
}
}
Object.assign(mainObj, tempObj);
return mainObj[newMethodName]();
}
const processPostfixes = (postfixes) => {
for (const postfix of postfixes) {
makeDynamic(postfix);
}
};
processPostfixes(postfixes);
console.log(mainObj);
Результат выполнения приведенного выше кода:
"called method instance: foo"
"called method instance: bar"
Object {
"instance: bar": [Function anonymous],
"instance: foo": [Function anonymous]
}
Спасибо, Маркоск! Основываясь на его ответе, если вы хотите переименовать любую функцию, используйте это:
// returns the function named with the passed name
function namedFunction(name, fn) {
return new Function('fn',
"return function " + name + "(){ return fn.apply(this,arguments)}"
)(fn)
}
Эта функция утилиты объединяет несколько функций в одну (с использованием пользовательского имени), только требование состоит в том, что при запуске и окончании его совпадающих функций функции будут правильно "новыми".
const createFn = function(name, functions, strict=false) {
var cr = `\n`, a = [ 'return function ' + name + '(p) {' ];
for(var i=0, j=functions.length; i<j; i++) {
var str = functions[i].toString();
var s = str.indexOf(cr) + 1;
a.push(str.substr(s, str.lastIndexOf(cr) - s));
}
if(strict == true) {
a.unshift('\"use strict\";' + cr)
}
return new Function(a.join(cr) + cr + '}')();
}
// test
var a = function(p) {
console.log("this is from a");
}
var b = function(p) {
console.log("this is from b");
}
var c = function(p) {
console.log("p == " + p);
}
var abc = createFn('aGreatName', [a,b,c])
console.log(abc) // output: function aGreatName()
abc(123)
// output
this is from a
this is from b
p == 123
Есть два способа достичь этого, и у них есть свои плюсы и минусы.
name
определение свойства Определение неизменяемого name
свойства для функции.
() 全 {}/1/얏호/ :D #GO(@*#%! /*
)name
.Создание именованной функции выражение и оценка с помощью конструктора Function
.
name
.(){}/1//
выражение return function (){}/1//() {}
, вместо функции. NaN
).const demoeval = expr => (new Function(`return ${expr}`))();
// `name` property definition
const method1 = func_name => {
const anon_func = function() {};
Object.defineProperty(anon_func, "name", {value: func_name, writable: false});
return anon_func;
};
const test11 = method1("DEF_PROP"); // No whitespace
console.log("DEF_PROP?", test11.name); // "DEF_PROP"
console.log("DEF_PROP?", demoeval(test11.toString()).name); // ""
const test12 = method1("DEF PROP"); // Whitespace
console.log("DEF PROP?", test12.name); // "DEF PROP"
console.log("DEF PROP?", demoeval(test12.toString()).name); // ""
// Function expression evaluation
const method2 = func_name => demoeval(`function ${func_name}() {}`);
const test21 = method2("EVAL_EXPR"); // No whitespace
console.log("EVAL_EXPR?", test21.name); // "EVAL_EXPR"
console.log("EVAL_EXPR?", demoeval(test21.toString()).name); // "EVAL_EXPR"
const test22 = method2("EVAL EXPR"); // Uncaught SyntaxError: Unexpected identifier
Мне повезло в объединении ответа Даррена и ответа кернетикоса.
const nameFunction = function (fn, name) {
return Object.defineProperty(fn, 'name', {value: name, configurable: true});
};
/* __________________________________________________________________________ */
let myFunc = function oldName () {};
console.log(myFunc.name); // oldName
myFunc = nameFunction(myFunc, 'newName');
console.log(myFunc.name); // newName
Если вы хотите иметь динамическую функцию, такую как функция __call
в PHP, вы можете использовать прокси.
const target = {};
const handler = {
get: function (target, name) {
return (myArg) => {
return new Promise(resolve => setTimeout(() => resolve('some' + myArg), 600))
}
}
};
const proxy = new Proxy(target, handler);
(async function() {
const result = await proxy.foo('string')
console.log('result', result) // 'result somestring' after 600 ms
})()
Я много боролся с этим вопросом. Решение @Albin работало как шарм при разработке, но не работало, когда я перешел на производство. После некоторой отладки я понял, как добиться того, что мне нужно. Я использую ES6 с CRA (create-реакции-приложение), что означает, что он в комплекте с Webpack.
Допустим, у вас есть файл, который экспортирует нужные вам функции:
myFunctions.js
export function setItem(params) {
// ...
}
export function setUser(params) {
// ...
}
export function setPost(params) {
// ...
}
export function setReply(params) {
// ...
}
И вам нужно динамически вызывать эти функции в другом месте:
myApiCalls.js
import * as myFunctions from 'path_to/myFunctions';
/* note that myFunctions is imported as an array,
* which means its elements can be easily accessed
* using an index. You can console.log(myFunctions).
*/
function accessMyFunctions(res) {
// lets say it receives an API response
if (res.status === 200 && res.data) {
const { data } = res;
// I want to read all properties in data object and
// call a function based on properties names.
for (const key in data) {
if (data.hasOwnProperty(key)) {
// you can skip some properties that are usually embedded in
// a normal response
if (key !== 'success' && key !== 'msg') {
// I'm using a function to capitalize the key, which is
// used to dynamically create the function name I need.
// Note that it does not create the function, it just a
// way to access the desired index on myFunctions array.
const name = 'set${capitalizeFirstLetter(key)}';
// surround it with try/catch, otherwise all unexpected properties in
// data object will break your code.
try {
// finally, use it.
myFunctions[name](data[key]);
} catch (error) {
console.log(name, 'does not exist');
console.log(error);
}
}
}
}
}
}
function myFunction() {
console.log('It works!');
}
var name = 'myFunction';
window[name].call();
Вы были рядом:
this["instance_" + a] = function () {...};
Возможно, я пропустил здесь очевидное, но что не так, просто добавив имя? функции вызываются независимо от их имени. имена используются только для выяснения причин. если вы назначаете его переменной, а в области видимости ее можно вызвать. происходит случай, когда вы выполняете переменную, которая является функцией. если у вас должно быть имя по причинам идентификации при отладке, вставьте его между функцией ключевого слова и открывающей скобкой.
var namedFunction = function namedFunction (a,b) {return a+b};
alert(namedFunction(1,2));
alert(namedFunction.name);
alert(namedFunction.toString());
Это решение BEST, лучше new Function('return function name(){}')()
.
Eval - это самое быстрое решение:
var name = 'FuncName'
var func = eval("(function " + name + "(){})")