Динамическое имя функции в JavaScript?

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

this.f = function instance(){};

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

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

24 ответа

Решение

Обновить

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

Вы можете использовать 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, как указано в Справочнике по 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

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

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, хакерские решения или обертки. Начиная с ES2015 имена выводятся из синтаксической позиции для переменных и свойств.

Так что это будет работать нормально:

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

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

Более подробный ответ со всеми изложенными вариантами был написан здесь: /questions/22265946/est-li-kakoj-nibud-nerealnyij-sposob-sozdat-funktsiyu-s-imenem-opredelennyim-vo-vremya-vyipolneniya/22265956#22265956

Функция name свойство по умолчанию не writeable, но поскольку это configurableмы все еще можем использовать, чтобы изменить это. С Object.defineProperty удобно возвращает сам объект, мы можем написать функцию с таким динамическим именем:

Над nameFunction Конечно, функция также может использоваться для переименования существующей функции (здесь она просто переименовывает и возвращает анонимную).

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

Демо

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

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

  • принимает объявленную функцию alredy
  • разбирает его на [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 не будет указывать имя. Обычно это проблема только для отладчиков.

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

Лучший способ - создать объект со списком динамических функций, например:

const USER = 'user';

const userModule = {
  [USER + 'Action'] : function () { ... }, 
  [USER + 'OnClickHandler'] : function () { ... }, 
  [USER + 'OnCreateHook'] : function () { ... }, 
}

Динамические методы объекта могут быть созданы с использованием расширений 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]
}

Вы можете использовать имя динамической функции и такие параметры.

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);

Если вы хотите иметь динамическую функцию, такую ​​как __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
})()

Есть два способа добиться этого, и у них есть свои плюсы и минусы.


name определение свойства

Определение неизменного name свойство функции.

Pros

  • Каждый персонаж доступен для имени. (например. () 全 {}/1/얏호/ :D #GO(@*#%! /*)

Cons

  • Синтаксическое ("экспрессивное") имя функции может не соответствовать name стоимость имущества.

Оценка выражения функции

Создание выражения именованной функции и оценка его с помощью Function конструктор.

Pros

  • Синтаксическое ("экспрессивное") имя функции всегда соответствует ее name стоимость имущества.

Cons

  • Пробелы (и т. Д.) Не доступны для имени.
  • Впрыскиваемое выражение (например, для ввода (){}/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

Я много боролся с этим вопросом. Решение @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'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 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

Мне повезло больше в сочетании ответа Даррена и ответа Кернетикоса.

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 делает то же самое.

  1. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name

Имя динамической функции на основе класса JavaScript Node.js

Файл: Schema.js

      class Schema {
  constructor() {
    this.name = null;
  }

  virtual(name = null) {
    this.name = name;
    return this;
  }

  get(func = false) {
    if (!this.name || !func instanceof Function) {
      throw new Error("Name and function must be provided.");
    }
    // Attach the dynamic function name to the "this" Object
    this[this.name] = func;
    this.name = null;
  }
}

module.exports = Schema;

Файл: index.js

      const Schema = require("./Schema.js");

const User = new Schema();

User.virtual("getPostCount").get(() => {
  return 10 + 10;
});

const ok = User.getPostCount();
console.log({ User });
console.log(ok);
function myFunction() {
    console.log('It works!');
}

var name = 'myFunction';

window[name].call();

Спасибо Маркос! Основываясь на его ответе, если вы хотите переименовать какую-либо функцию, используйте это:

// 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)
}

Вы были рядом:

this["instance_" + a] = 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};

Это ЛУЧШЕЕ решение, лучше чем new Function('return function name(){}')(),

Eval - самое быстрое решение:

введите описание изображения здесь

var name = 'FuncName'
var func = eval("(function " + name + "(){})")
Другие вопросы по тегам