У меня есть это:
this.f = function instance(){};
Я бы хотел вот это:
this.f = function ["instance:" + a](){};
У меня есть это:
this.f = function instance(){};
Я бы хотел вот это:
this.f = function ["instance:" + a](){};
this["instance" + a] = function() { }
. Мне это было непонятно.
Ответы:
Как уже упоминалось, это не самое быстрое и не самое рекомендуемое решение. Решение Маркоса, приведенное ниже, - это правильный путь.
Вы можете использовать eval:
var code = "this.f = function " + instance + "() {...}";
eval(code);
eval()
( Function
конструктор делает это внутри).
Это в основном сделает это на самом простом уровне:
"use strict";
var name = "foo";
var func = new Function(
"return function " + name + "(){ alert('sweet!')}"
)();
//call it, to test it
func();
Если вы хотите получить больше фантазии, я написал статью « Динамические имена функций в JavaScript ».
eval
для оценки javascript, открывая тем самым ваш код для множества уязвимостей.
Вы можете использовать Object.defineProperty, как указано в MDN JavaScript Reference [1]:
var myName = "myName";
var f = function () { return true; };
Object.defineProperty(f, 'name', {value: myName, writable: false});
function fn()
, fn
будучи оригинальным названием. Странно.
В последних двигателях вы можете делать
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"
Object.defineProperty(func, 'name', {value: name})
в своем собственном коде, так как думаю, что он может быть немного более естественным и понятным.
{[expr]: val}
инициализатор объекта (как объект JSON), где expr
какое-то выражение; все, что он оценивает, является ключевым. {myFn (..){..} }
это сокращение для {myFn: function myFn(..){..} }
. Обратите внимание, что function myFn(..) {..}
может использоваться как выражение, как анонимная функция, только myFn
будет иметь имя. Последнее [name]
- это просто доступ к члену объекта (как obj.key
или obj['key']
). ...
- оператор распространения. (Основной источник: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… )
this
. Например obj={x:7,getX(){return this.x}}; obj.getX=nameFunction('name',obj.getX); obj.getX();
не сработает. Вы можете отредактировать свой ответ и использовать function nameFunction(name, body) { return {[name](...args) {return body.apply(this, args)}}[name] }
вместо него!
Я думаю, что большинство предложений здесь неоптимальны с использованием eval, хакерских решений или оберток. Начиная с ES2015 имена выводятся из синтаксической позиции для переменных и свойств.
Так что это будет работать нормально:
const name = 'myFn';
const fn = {[name]: function() {}}[name];
fn.name // 'myFn'
Не поддавайтесь искушению создать методы фабрики именованных функций, поскольку вы не сможете передать функцию извне и модифицировать ее в синтаксической позиции, чтобы вывести ее имя. Тогда уже поздно. Если вам это действительно нужно, вам нужно создать оболочку. Кто-то сделал это здесь, но это решение не работает для классов (которые также являются функциями).
Здесь написан гораздо более подробный ответ со всеми описанными вариантами: https://stackoverflow.com/a/9479081/633921
Что о
this.f = window["instance:" + a] = function(){};
Единственный недостаток заключается в том, что функция в методе toSource не будет указывать имя. Обычно это проблема только отладчиков.
Синтаксис 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.
(name => ({[name]:function(){}})[name])('test')
работает, но (name => {var x={}; x[name] = function(){}; return x[name];})('test')
не работает
Ответ, получивший наибольшее количество голосов, уже имеет тело функции [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){...}
Динамические методы объекта могут быть созданы с использованием расширений литералов объекта, предоставляемых 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]
}
o={}; o[name]=(()=>{})
а не наfunction <<name>>(){}
Для установки имени существующей анонимной функции:
(на основе ответа @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
Демо .
Примечание : не делайте этого; /
Есть два способа добиться этого, и у них есть свои плюсы и минусы.
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
Если вы хотите иметь динамическую функцию, такую как __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
})()
Вы можете использовать имя динамической функции и такие параметры.
1) Определите функцию Separate и вызовите ее
let functionName = "testFunction";
let param = {"param1":1 , "param2":2};
var func = new Function(
"return " + functionName
)();
func(param);
function testFunction(params){
alert(params.param1);
}
2) Определите динамический код функции
let functionName = "testFunction(params)";
let param = {"param1":"1" , "param2":"2"};
let functionBody = "{ alert(params.param1)}";
var func = new Function(
"return function " + functionName + functionBody
)();
func(param);
Спасибо, Маркоск! Основываясь на его ответе, если вы хотите переименовать какую-либо функцию, используйте это:
// 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
У меня была лучшая удача в сочетании ответ Даррена и ответ kyernetikos игровая .
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
Примечание: configurable
установлено в true
соответствии со стандартной спецификацией ES2015 для Function.name 1
Это особенно помогло обойти ошибку в Webpack, подобную этой. .
Обновление: я думал опубликовать это как пакет npm, но этот пакет от sindresorhus делает то же самое.
Я много боролся с этой проблемой. Решение @Albin работало как шарм при разработке, но оно не сработало, когда я изменил его на производство. После некоторой отладки я понял, как добиться того, что мне нужно. Я использую ES6 с CRA (create-react-app), что означает, что он связан с 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's name I need.
// Note that it does not create the function, it's 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);
}
}
}
}
}
}
лучший способ - создать объект со списком динамических функций, например:
const USER = 'user';
const userModule = {
[USER + 'Action'] : function () { ... },
[USER + 'OnClickHandler'] : function () { ... },
[USER + 'OnCreateHook'] : function () { ... },
}
function myFunction() {
console.log('It works!');
}
var name = 'myFunction';
window[name].call();
Я могу упустить очевидное здесь, но что плохого в том, чтобы просто добавить имя? функции вызываются независимо от их имени. имена используются только для целей определения объема. если вы назначите его переменной, и она находится в области видимости, ее можно будет вызвать. это происходит, когда вы выполняете переменную, которая оказывается функцией. если вам необходимо иметь имя для идентификации при отладке, вставьте его между ключевым словом function и открывающей скобкой.
var namedFunction = function namedFunction (a,b) {return a+b};
alert(namedFunction(1,2));
alert(namedFunction.name);
alert(namedFunction.toString());
Альтернативный подход - заключить функцию во внешнюю переименованную прокладку, которую вы также можете передать во внешнюю оболочку, если вы не хотите загрязнять окружающее пространство имен. если вы действительно хотите динамически создавать функцию из строк (что и делается в большинстве этих примеров), тривиально переименовать источник, чтобы делать то, что вы хотите. Однако если вы хотите переименовать существующие функции, не влияя на их функциональность при вызове в другом месте, прокладка - единственный способ добиться этого.
(function(renamedFunction) {
alert(renamedFunction(1,2));
alert(renamedFunction.name);
alert(renamedFunction.toString());
alert(renamedFunction.apply(this,[1,2]));
})(function renamedFunction(){return namedFunction.apply(this,arguments);});
function namedFunction(a,b){return a+b};
name
полезны, поскольку теперь выводятся из переменных и свойств. Он также используется в трассировке стека. Ex var fn = function(){}; console.log(fn.name)
. Он неизменен, поэтому вы не можете изменить его позже. Если вы напишете фабричный метод, который называет все функции, fn
это усложнит отладку.
this["instance"] = function() { }