javascript中的动态函数名称?

时间:2011-05-06 00:11:24

标签: javascript function

我有这个:

this.f = function instance(){};

我想有这个:

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

22 个答案:

答案 0 :(得分:106)

这基本上是在最简单的层面上完成的:

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

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

如果你想得到更多的幻想,我写了一篇关于“Dynamic function names in JavaScript”的文章。

答案 1 :(得分:28)

您可以使用MDN JavaScript参考[1]中所述的Object.defineProperty:

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

答案 2 :(得分:18)

在最近的引擎中,你可以做到

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"

答案 3 :(得分:7)

我认为通过使用eval,hacky解决方案或包装器,这里的大多数建议都不是最理想的。 从ES2015开始,名称是从变量和属性的句法位置推断出来的。

所以这样可以正常工作:

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

抵制创建命名函数工厂方法的诱惑,因为您无法从外部传递函数并将其改进到语法位置以推断其名称。那已经太晚了。如果你真的需要它,你必须创建一个包装器。有人这样做了,但是这个解决方案对于类(也是函数)不起作用。

这里写的所有变体的更深入的答案都写在这里:https://stackoverflow.com/a/9479081/633921

答案 4 :(得分:6)

更新

正如其他人所说,这不是最快或最推荐的解决方案。 Marcosc's solution below是要走的路。

您可以使用eval:

var code = "this.f = function " + instance + "() {...}";
eval(code);

答案 5 :(得分:3)

语法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()
);

在FireFox中显示({f:(function () {})}) (这与this solution几乎相同,只是它使用通用对象,不再使用函数直接填充窗口对象。)

此方法使用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,但此方法避免了this solution的几个问题。

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示例的语义和语法维护全局计算环境。

答案 6 :(得分:3)

最投票的答案已经定义了[String]函数体。我正在寻找重命名已经声明的函数名称的解决方案,最后经过一个小时的挣扎我已经处理过了。它:

  • 采用alredy声明的功能
  • 使用.toString()方法
  • 将其解析为[String]
  • 然后覆盖名称​​(已命名的函数)或在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){...}

答案 7 :(得分:2)

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

唯一的缺点是其toSource方法中的函数不会指示名称。这通常只是调试器的一个问题。

答案 8 :(得分:2)

用于设置现有匿名函数的名称:
(根据@ Marcosc的回答)

a-z

Demo

注意 :不要这样做; /

答案 9 :(得分:2)

可以使用ECMAScript 2015(ES6)提供的Object Literal Extensions创建对象的动态方法:

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]
}

答案 10 :(得分:1)

您可以像这样使用动态功能名称和参数。

1)定义函数并单独调用

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

答案 11 :(得分:0)

最好的方法是创建具有动态功能列表的对象,例如:

const USER = 'user';

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

答案 12 :(得分:0)

我在这个问题上苦苦挣扎。 @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);
          }
        }
      }
    }
  }
}

答案 13 :(得分:0)

如果您希望拥有动态功能,例如PHP中的__call函数,则可以使用代理。

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

答案 14 :(得分:0)

我更好地将Darren's answerkyernetikos's answer结合起来。

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以匹配Function.name 1 的标准ES2015规范

这特别有助于解决Webpack中与this one类似的错误。

更新:我考虑将其发布为npm包,但this package from sindresorhus完全相同。

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

答案 15 :(得分:0)

实现这一目标有两种方法,它们各有利弊。

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

答案 16 :(得分:0)

此实用程序功能将多个功能合并为一个(使用自定义名称),唯一的要求是提供的功能在其独家新闻的开头和结尾处正确“新线”。

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

答案 17 :(得分:0)

谢谢Marcosc!根据他的回答,如果你想重命名任何功能,请使用:

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

答案 18 :(得分:-1)

function myFunction() {
    console.log('It works!');
}

var name = 'myFunction';

window[name].call();

答案 19 :(得分:-2)

我可能会错过这里显而易见的,但是添加名称有什么不对?无论名称如何,都会调用函数。名称仅用于范围界定原因。如果将它分配给变量,并且它在范围内,则可以调用它。帽子发生的是你正在执行一个碰巧是函数的变量。如果在调试时必须有识别名称的名称,请将其插入关键字功能和左大括号之间。

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

答案 20 :(得分:-2)

你在附近:

this["instance_" + a] = function () {...};

{...};

答案 21 :(得分:-9)

这是最佳解决方案,优于new Function('return function name(){}')()

Eval是最快的解决方案:

enter image description here

var name = 'FuncName'
var func = eval("(function " + name + "(){})")