javascriptの動的関数名?


89

私はこれを持っています:

this.f = function instance(){};

私はこれが欲しいです:

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

10
できません。しかし、あなたは持つことができますthis["instance"] = function() { }
Raynos


Raynosが言っていたことを明確にするために、あなたはすることができますthis["instance" + a] = function() { }。それは私にはわかりませんでした。
アンドリュー

回答:


8

更新

他の人が述べたように、これは最速でも最も推奨される解決策でもありません。以下のMarcoscのソリューションがその方法です。

evalを使用できます:

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

1
それが私が求めたものです、ありがとう!(とにかく私は遅すぎるのでこの機能を使用しません)
Totty.js

3
私はこれがOPが求めたものであることを知っていますが、これは恐ろしい考えです。できないからといって、このようなことをしなければならないという意味ではありません。機能的にはほぼ同じである、はるかに優れた代替手段があります。
Thomas Eding

4
@ sg3s:別の解決策を提案できますか?
トム

2
@ sg3s:私のコメントに答えてくれてありがとう!私が何を意味し、実際に何を聞きたいのかを説明しましょう。Marcoscのソリューションはevalとは本当に大きく異なりますか?何かを評価したり、関数コンストラクターにフィードしたりすると、本当に違いがありますか?もしそうなら、違いとその影響を説明できますか?ありがとう!
トム

5
@Tom将来の読者のために:このソリューションはMarcoscの答えと実際には違いはなく、両方とも使用しますeval()Functionコンストラクターが内部でそれを行います)。
kapa 2015年

128

これは基本的に最も単純なレベルでそれを行います:

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

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

もっと凝ったものにしたい場合は、「JavaScriptの動的関数名」に関する記事を書いています


よくやった!私はこれを自分で発見し、これらの質問の1つに投稿しようとしていましたが、あなたは私をそれに打ち負かしました。私は最近backbone.jsで多くの作業を行っており、Chromeデバッガーのいたるところに「子」が表示されることにうんざりしています。これはその問題を解決します。evalの使用のようなパフォーマンスへの影響はあるのでしょうか。
webXL 2012

セキュリティへの影響もあります。jsHintから「関数コンストラクターはevalです」を取得するので、これをデバッグモードチェックでラップします。これを使用する唯一の理由です。「usestrict」を使用すると、グローバルオブジェクトの不正行為を防ぐことができると思いますが、Functionコンストラクターへの引数は変更でき、「this」に設定されているものは何でも変更できます。
webXL 2012

はい、これは、その場で何かを構築する必要がある極端な状況に対応します。
Marcosc 2013

1
私は正直に言って、stackoverflow.com / a / 40918734/2911851がより良い解決策であり、関数の本体を文字列として含める必要はないと思います(何かが足りない場合を除いて、私は試しただけでうまくいきました)。
Gian Franco Zabarino 2017

2
関数コンストラクターがevaljavascriptを評価するために使用するため、AirBnBはこれ反対することを強くお勧めします。したがって、コードは多数の脆弱性にさらされます。
Philippe Hebert 2017年

43

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
印刷し意図したとおり、このセットに名前のプロパティを表示され、まだ私のブラウザ(Firefoxの最新のdevの版)に機能をIを記録した場合function fn()fn元の名前であること。奇妙な。
Kiara Grouwstra 2017年

グーグルクロームエバーグリーンについての同じコメント。プロパティは正しく設定されていますが、コンソールの名前は関数の元の名前です。Cf. 2ality.com/2015/09/… 関数の名前は作成時に割り当てられており、変更できないようです。
user3743222 2018年

その2ality.comページで、MDNJavascriptリファレンスにあるのと同じ手法を説明する次の段落「関数の名前の変更」[1]を参照してください。1. 2ality.com/2015/09/…–
ダレン

36

最近のエンジンでは、

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"


1
解決策を見つけるために何時間も費やした後、私は計算されたプロパティ名を持つオブジェクトを使用することさえ考えませんでした。まさに私が必要としていたもの。ありがとう!
Levi Roberts

7
これは機能しObject.defineProperty(func, 'name', {value: name})ますが、もう少し自然で理解しやすいと思うので、自分のコードで使用し始めました。
kybernetikos 2017年

トランスパイルされたコードで動作しますか?(Babelまたはtypescriptの出力)
Hitmands 2017

3
私自身の質問に答える:{[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/...
ジェイソン・ヤング

1
あなたの解決をありがとう!注:これはの値を保持しません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] }代わりに使うことができます!
TS

11

ここでのほとんどの提案は、評価、ハッキーなソリューション、またはラッパーを使用することにより、最適ではないと思います。ES2015の時点では、名前は変数とプロパティの構文上の位置から推測されます。

したがって、これは問題なく機能します。

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

名前付き関数ファクトリメソッドを作成したいという誘惑に抵抗してください。外部から関数を渡して、その名前を推測するために構文上の位置に組み込むことができないためです。それならもう手遅れです。本当に必要な場合は、ラッパーを作成する必要があります。誰かがここでそれをしましたが、その解決策はクラス(関数でもあります)では機能しません。

概説されているすべてのバリアントを含むはるかに詳細な回答がここに書かれています:https//stackoverflow.com/a/9479081/633921


1
この回答はstackoverflow.com/a/41854075/3966682をどのように改善しますか?
d4nyll 2018

1
@ d4nyll私はすでに答えました:それは、ステートフル関数(「this」を使用する関数)、別名クラスを壊すラッパーを作成します。
アルビン

@Albin FWIW、あなたの答えがそれをどのように言っているのか私にはまったくわかりません。いずれにせよ、それは知っておくと良いことです。
ジェイソンヤング

@JasonYoung "名前付き関数のファクトリメソッドを作成する誘惑に抵抗してください。外部から関数を渡して構文上の位置に修正し、名前を推測することはできません。それではもう手遅れです。本当に必要な場合は、ラッパーを作成する必要があります。誰かがここでそれを行いましたが、そのソリューションはクラス(関数でもあります)では機能しません。」
アルビン

3

どうですか

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

唯一の欠点は、toSourceメソッドの関数が名前を示さないことです。これは通常、デバッガーの問題にすぎません。


私が必要とする唯一の理由はクラスの名前をより速く見ることなので、それは良くありません。私のシステム内のすべてのクラスが匿名関数であり、デバッガのショーで私は匿名の...
Totty.js

1
さて、あなたは質問でそれを言うことができたでしょう。
entonio 2012年

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

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

プロパティ関数fanonymous 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の例のセマンティクスと構文を可能な限り使用して、グローバルコンピューティング環境を維持します。


この解決策は、静的名またはES6動的プロパティ名でのみ機能します。たとえば、(name => ({[name]:function(){}})[name])('test')機能しますが(name => {var x={}; x[name] = function(){}; return x[name];})('test')機能しません
William Leung

3

最も投票された回答は、すでに定義されている[String]関数本体を持っています。すでに宣言されている関数の名前の名前を変更するソリューションを探していましたが、1時間苦労した後、ついに対処しました。それ:

  • すでに宣言された関数を取ります
  • .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){...}


2

オブジェクトの動的メソッドは、ECMAScript 2015(ES6)によって提供されるObject LiteralExtensionsを使用して作成できます。

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>>(){}
Sancarn 2018

2

既存の無名関数の名前を設定する場合:
(@ 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

デモ

:それをしないでください; /


あなたが反対票を投じるとき、それは大丈夫です。しかし、私たちがあなたから学ぶことができるように、あなたはあなたの推論の礼儀を与えるべきです。OPは「動的」関数名を要求します。これはそれを行う1つの方法ですが、私は決してお勧めしたり、したことはありません。
オヌルユルドゥルム

1

これを実現するには2つの方法があり、それぞれに長所と短所があります。


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

1

__callPHPの関数のような動的関数が必要な場合は、プロキシを使用できます。

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

このような動的関数名とパラメーターを使用できます。

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

0

マルコスクありがとう!名前を変更したい場合は彼の答えを踏まえ、すべての機能を、これを使用します。

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

0

このユーティリティ関数は、(カスタム名を使用して)複数の関数を1つにマージします。唯一の要件は、提供された関数がスクープの開始時と終了時に適切に「新しい行」であるということです。

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

0

ダレンの答え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に設定されているtrueFunction.nameための標準ES2015の仕様に合わせて、1

これは、特にに類似のWebPACKでエラーが周りを取得して助けたこれを

更新:これをnpmパッケージとして公開することを考えていましたが、sindresorhusのこのパッケージはまったく同じことを行います。

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

0

私はこの問題で多くの苦労をしました。@Albinソリューションは、開発中は魅力のように機能しましたが、本番環境に変更すると機能しませんでした。いくつかのデバッグの後、私は必要なことを達成する方法に気づきました。私はCRA(create-react-app)でES6を使用しています。これは、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);
          }
        }
      }
    }
  }
}


0

次のような動的関数のリストを使用してオブジェクトを作成するのが最善の方法です。

const USER = 'user';

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


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


関数/メソッドnameは、変数とプロパティから推測されるようになったので便利です。スタックトレースでも使用されます。例var fn = function(){}; console.log(fn.name)。不変なので、後で変更することはできません。すべての関数に名前を付けるファクトリメソッドをfn作成すると、デバッグが難しくなります。
アルビン

-3

あなたは近くにいました:

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

{...};


-9

これが最良の解決策であり、それよりも優れていnew Function('return function name(){}')()ます。

Evalは最速のソリューションです:

ここに画像の説明を入力してください

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

これを読んでいる人は誰でも、この答えに従ってください。
maxmaxmaximus 2016

1
@majidarif男が正しければ、それが最速です:jsperf.com/dynamicfunctionnames/1。しかし、これまでで最も安全ではありません。
サンカーン2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.