Динамическое имя функции в JavaScript? - PullRequest
68 голосов
/ 06 мая 2011

У меня есть это:

this.f = function instance(){};

Я хотел бы иметь это:

this.f = function ["instance:" + a](){};

Ответы [ 20 ]

106 голосов
/ 30 марта 2012

В основном это будет сделано на самом простом уровне:

"use strict";
var name = "foo";
var func = new Function(
     "return function " + name + "(){ alert('sweet!')}"
)();

//call it, to test it
func();

Если вы хотите получить больше фантазии, я написал статью на тему " Имена динамических функций в JavaScript ".

29 голосов
/ 01 декабря 2016

Вы можете использовать Object.defineProperty, как указано в Справочнике по MDN JavaScript [1]:

var myName = "myName";
var f = function () { return true; };
Object.defineProperty(f, 'name', {value: myName, writable: false});
  1. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name#Description
20 голосов
/ 25 января 2017

В последних двигателях вы можете сделать

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"
7 голосов
/ 21 февраля 2018

Я думаю, что большинство предложений здесь неоптимально, с использованием eval, хакерских решений или оболочек. Начиная с ES2015 имена выводятся из синтаксической позиции для переменных и свойств.

Так что это будет прекрасно работать:

const name = 'myFn';
const fn = {[name]: function() {}}[name];
fn.name // 'myFn'

Не поддавайтесь искушению создать методы фабрики именованных функций, поскольку вы не сможете передать функцию извне и перевести ее в синтаксическую позицию, чтобы вывести ее имя. Тогда уже слишком поздно. Если вам это действительно нужно, вы должны создать оболочку. Кто-то сделал это здесь, но это решение не работает для классов (которые также являются функциями).

Более подробный ответ со всеми изложенными вариантами был написан здесь: https://stackoverflow.com/a/9479081/633921

6 голосов
/ 06 мая 2011

обновление

Как уже упоминалось, это не самое быстрое и не рекомендуемое решение. Решение Маркоса ниже - путь.

Вы можете использовать eval:

var code = "this.f = function " + instance + "() {...}";
eval(code);
3 голосов
/ 17 ноября 2017

Самый проголосовавший ответ уже определил тело функции [String].Я искал решение переименовать имя уже объявленной функции и, наконец, после часа борьбы с этим я справился.Он:

  • принимает объявленную ранее функцию
  • , анализирует ее в [String] с .toString() методом
  • , затем переписывает имя (названной функции) или добавляет новый (когда анонимный) между function и (
  • , затем создает новую переименованную функцию с помощью new Function() constructor

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){...}
3 голосов
/ 24 мая 2011

Синтаксис 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.

2 голосов
/ 15 мая 2016

Для установки имени существующей анонимной функции:
(Основываясь на ответе @ 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

Демо .

Примечание : не делайте этого; /

2 голосов
/ 29 января 2017

Динамические методы объекта могут быть созданы с использованием расширений Object Literal, предоставляемых 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]
}
2 голосов
/ 06 мая 2011

А как же

this.f = window["instance:" + a] = function(){};

Единственным недостатком является то, что функция в методе toSource не будет указывать имя. Обычно это проблема только для отладчиков.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...