动态函数名在 javascript? ?

我有这个:

this.f = function instance(){};

我想要这个:

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

正如其他人所提到的,这不是最快也不是最推荐的解决方案。

You can use eval:

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

关于什么

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

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

语法 function[i](){}意味着对象的属性值是函数 function[],通过名称 [i]进行索引。
Thus
{"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]将保留函数名标识。请参阅下面关于 :的说明。

So,

javascript: alert(
new function(a){
this.f={"instance:1":function(){}, "instance:A":function(){}} ["instance:"+a]
}("A") . toSource()
);

在 FireFox 中显示 ({f:(function () {})})
(这与 这个解决方案的思想几乎相同,只是它使用了一个通用对象,不再直接用函数填充窗口对象。)

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

这基本上可以在最简单的层面上做到:

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


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

如果你想变得更花哨,我写了一篇关于“ JavaScript 中的动态函数名”的文章。

谢谢 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)
}
function myFunction() {
console.log('It works!');
}


var name = 'myFunction';


window[name].call();

用于设置 存在匿名函数的名称:
(Based on @Marcosc's answer)

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

演示。

注意 : 不要这样做;/

这是最好的解决方案,比 new Function('return function name(){}')()更好。

Eval 是最快的解决方案:

enter image description here

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

可以按照 MDN JavaScript 参考中的说明使用 Object.defeProperty:

var myName = "myName";
var f = function () { return true; };
Object.defineProperty(f, 'name', {value: myName, writable: false});

在最近的引擎,你可以做

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"

可以使用 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]
}

你就在附近:

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

{...};

这个实用程序函数将多个函数合并为一个(使用自定义名称) ,唯一的要求是所提供的函数在其勺子的开始和结束处都是正确的“新行”。

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

There are two methods to achieve this, and they have their pros and cons.


name属性定义

定义函数的 不可变 name 财产

优点

  • 每个字符都可用于名称(例如 () 全 {}/1/얏호/ :D #GO(@*#%! /*)

缺点

  • 函数的 句法(“表达式”)名称可能与其 name属性值不一致。

函数表达式计算

Function构造函数构造一个 名字函数 表情评估

优点

  • 函数的 syntactic (“expressional”) name始终与其 name属性值对应。

缺点

  • 名称不可用空白(等等)。
  • Expression-injectable (eg. For input (){}/1//, the expression is return function (){}/1//() {}, gives NaN instead of a function.).

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

投票最多的答案已经定义了[ String ]函数体。我一直在寻找重命名已声明函数名的解决方案,经过一个小时的努力,我终于解决了这个问题。它:

  • 接受已经声明的函数
  • 使用 .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){...}

我可能忽略了一个显而易见的事实,但是加上这个名字又有什么错呢?函数的调用与它们的名称无关。名称仅用于范围原因。如果你把它赋值给一个变量,并且它在作用域中,就可以调用它。就是你正在执行一个变量而这个变量恰好是一个函数。如果在调试时由于标识原因必须有一个名称,请将其插入到关键字函数和大括号之间。

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

更新2021: CherryDT 的回答现在应该是最简单直接的方法,但是它不能在不同的浏览器上一致地进行堆栈跟踪或 Function.Prototype.toString () ,所以如果你需要的话,你只能使用这个不太方便的解决方案。

旧的答案: 这里的许多建议是次优的,通过使用 eval、 hacky 解决方案或者 wrappers。 As of ES2015 names are inferred from the syntactic position for variables and properties.

因此,这种方法会很有效:

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

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

一个更加深入的答案与所有的变体概述已经写在这里: https://stackoverflow.com/a/9479081/633921

我在结合 Darren's answerKyernetikos 的回答方面运气比较好。

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.name1的标准 ES2015规范

这特别有助于避免 Webpack 中类似于 这个的错误。

更新: 我本来想把它作为 npm 包发布,但是 这个包裹是从 Sindreshus 寄来的也做同样的事情。

  1. Https://developer.mozilla.org/en-us/docs/web/javascript/reference/global_objects/function/name

如果希望在 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
})()

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


您可以像这样使用动态函数名和参数。

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

the best way it is create object with list of dynamic functions like:

const USER = 'user';


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

默认情况下,函数的 name属性不是 writeable,但是因为它是 configurable,所以我们仍然可以使用 Object.defineProperty来更改它。由于 Object.defineProperty方便地返回对象本身,我们可以编写一个动态名称如下的函数:

const theName = 'foobar'


const fn = Object.defineProperty(function () {
/* ... */
}, 'name', { value: theName })


console.log(fn.name) // Logs foobar

当然,这可以分解成一个 helper 函数:

const nameFunction = (name, fn) => Object.defineProperty(fn, 'name', { value: name })


const fn = nameFunction('foobar', function () {
/* ... */
})


console.log(fn.name) // Logs foobar

当然,上面的 nameFunction函数也可以用来重命名现有的函数(这里只是重命名并返回匿名函数)。

Node.js JavaScript Class Based Dynamic Function Name

文件: 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);