У мене є таке:
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 [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, hacky або обгортки. Станом на 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
посилається на a, 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() {})})
:
робить javascriptfunction 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-response-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();
Можливо, тут мені не вистачає очевидного, але що поганого у простому додаванні імені? функції викликаються незалежно від їх назви. імена просто використовуються з метою визначення сфери дії. якщо ви призначите його змінній, і вона перебуває в області дії, її можна викликати. hat відбувається - це те, що ви виконуєте змінну, яка є функцією. якщо під час налагодження вам потрібно вказати ім'я для ідентифікації, вставте його між функцією ключового слова та початковою дужкою.
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
є корисним, оскільки зараз випливає із змінної та властивостей. Він також використовується в слідах стека. Напр var fn = function(){}; console.log(fn.name)
. Він незмінний, тому ви не можете змінити його пізніше. Якщо ви пишете заводський метод, який називає всі функції, fn
то це ускладнить налагодження.
this["instance"] = function() { }