名前を文字列として持っているときにJavaScript関数を実行する方法


1051

JavaScriptの関数名を文字列として持っています。後で呼び出すことができるように、それを関数ポインターに変換するにはどうすればよいですか?

状況によっては、メソッドにさまざまな引数を渡す必要がある場合もあります。

一部の関数はの形式を取る場合がありnamespace.namespace.function(args[...])ます。

回答:


1438

絶対に、積極的に他に選択の余地がないeval限り使用しないでください。

すでに述べたように、次のようなものを使用するのが最善の方法です。

window["functionName"](arguments);

ただし、名前空間付きの関数では機能しません。

window["My.Namespace.functionName"](arguments); // fail

これは、次のようにする方法です。

window["My"]["Namespace"]["functionName"](arguments); // succeeds

それをより簡単にし、ある程度の柔軟性を提供するために、ここに便利な関数があります:

function executeFunctionByName(functionName, context /*, args */) {
  var args = Array.prototype.slice.call(arguments, 2);
  var namespaces = functionName.split(".");
  var func = namespaces.pop();
  for(var i = 0; i < namespaces.length; i++) {
    context = context[namespaces[i]];
  }
  return context[func].apply(context, args);
}

次のように呼び出します。

executeFunctionByName("My.Namespace.functionName", window, arguments);

あなたが望むどんなコンテキストでも渡すことができるので、これは上記と同じことになることに注意してください:

executeFunctionByName("Namespace.functionName", My, arguments);

4
「func」コンストラクト全体が必要ないことをご存知ですか?「context.apply」だけでも問題あり
ません

16
確かに、私はそれを知っています-しかし、私が関数を書いた方法は、それを読んでいる人に、何が起こっているのかを完全に理解していないかもしれないいくつかの明快さを提供します。私はこの関数を書いて、それを読んでいる人がいくらかの助けを必要とするかもしれないと気づきました。あなたが尋ねたので、私は代わりに私が提供します...
Jason Bunting

108
スクラッチ-コードは十分に明確で、知っている人は知っています。もしあなたが私のようなもので、あなたが何をしているのかを知っているなら、このコードを使用すれば自分でそのような変更を加えることができます。Stack Overflowは他の人を教育するためのものであり、私のコードは初心者にとって理解しやすいと思います。ありがとう!
Jason Bunting

4
window ["funcName"]が未定義を返す状況はありますか?それが今私が抱えている問題です。呼び出しコードと関数は、2つの別個のjsファイルで定義されます。同じファイルに追加してみましたが、違いはありませんでした。
codemonkey

5
ここに問題があると思います。あなたが呼び出すとMy.Namespace.functionName()thisを参照するMy.Namespaceオブジェクト。ただし、を呼び出すと、同じものを参照するexecuteFunctionByName("My.Namespace.functionName", window)方法はありませんthis。たぶん、最後の名前空間をスコープとして使用するかwindow、名前空間がない場合。または、ユーザーが引数としてスコープを指定できるようにすることもできます。
JW。

100

Jason Buntingの非常に便利な機能を少し変更したバージョンを投稿すると思いました。

最初に、slice()に 2番目のパラメーターを指定して、最初のステートメントを簡略化しました。元のバージョンは、IEを除くすべてのブラウザーで正常に動作していました。

次に、これをreturnステートメントのコンテキストに置き換えました。それ以外の場合、これはターゲット関数が実行されているときに常にウィンドウを指していました。

function executeFunctionByName(functionName, context /*, args */) {
    var args = Array.prototype.slice.call(arguments, 2);
    var namespaces = functionName.split(".");
    var func = namespaces.pop();
    for (var i = 0; i < namespaces.length; i++) {
        context = context[namespaces[i]];
    }
    return context[func].apply(context, args);
}

「functionName」が実際に存在するかどうかを確認するチェックはありませんか?
Crashalot 2016年

Macの答えは過小評価されていると思います。私は専門家ではありませんが、よく考えられており、堅固です。
マーティンハンセンレノックス

65

この他の質問に対する答えは、その方法を示しています。Pythonのlocals()に相当するJavascriptですか?

基本的に、あなたは言うことができます

window["foo"](arg1, arg2);

または他の多くの人が示唆しているように、あなたはevalを使うだけです:

eval(fname)(arg1, arg2);

何を評価するかについて絶対的に確信がない限り、これは非常に危険です。


6
最初の形式がはるかに望ましい
annakata 2008

19
他のすべてが失敗した場合にのみ、最後の手段としてevalを使用してください。
Jason Bunting

1
それは...しかし、次のような関数で動作しますか:xyz(args)?
Kieron、

@keiron:はい。以下の私の答えを参照してください
annakata 2008

55

これを行うことはできませんか?

var codeToExecute = "My.Namespace.functionName()";
var tmpFunc = new Function(codeToExecute);
tmpFunc();

このメソッドを使用して、他のJavaScriptを実行することもできます。


3
関数で引数が渡された場合にも機能します
adeel41

関数の戻りはどうですか?
Peter Denev 2014年

12
それはどう違うのeval("My.Namespace.functionName()");ですか?
developerbmw

@PeterDenevは、最初の行を次のように変更しますvar codeToExecute = "return My.Namespace.functionName()";
developerbmw

2
@developerbmw、ここで回答ですstackoverflow.com/questions/4599857/...は
Tejasvi Hegde

48

エレガントな方法は、ハッシュオブジェクトで関数を定義することだと思います。次に、文字列を使用して、ハッシュからこれらの関数への参照を持つことができます。例えば

var customObject = {
  customFunction: function(param){...}
};

その後、呼び出すことができます:

customObject['customFunction'](param);

ここで、customFunctionは、オブジェクトで定義された関数に一致する文字列になります。


@ibsenv、この回答が最良であると私が判断するのに役立つコメントをありがとうございます。関数オブジェクトの配列を作成し、それを使用してdeferred.promisesの配列を作成しました。以下にサンプルコードをいくつか示します。(私は新しい返信を作成しルーベンの返答を借りたくありませんでした。)
user216661

function getMyData(arrayOfObjectsWithIds){var functionArray = arrayOfObjectsWithIds.map(function(value){return {myGetDataFunction:MyService.getMyData(value.id)};})var promises = functionArray.map(function(getDataFunction){var deferred = $ q.defer(); getDataFunction.myGetDataFunction.success(function(data){deferred.resolve(data)})。error(function(error){deferred.reject();}); return deferred.promise;}); $ q.all(promises).then(function(dataArray){//何かを行う})};
user216661

これは素晴らしい働きをします。機能かどうかを確認するためにアンダースコア/ロダッシュのみを追加します。そして、実行
elporfirio 2016

35

ES6を使用すると、クラスメソッドに名前でアクセスできます。

class X {
  method1(){
    console.log("1");
  }
  method2(){
    this['method1']();
    console.log("2");
  }
}
let x  = new X();
x['method2']();

出力は次のようになります。

1
2

1
最高のjavascript ピュア ...神..機能していないクラスを削除しますが、問題ありません。ありがとう!
KingRider

1
久々に探していたものです。ありがとう!
PaladiN

ES2015はここでは何もしません。純粋なオブジェクト、またはを介しObject.create()たプロトタイプ委任を使用して、同じ目標を達成できます。const myObj = {method1(){console.log( '1')}、method2(){console.log( '2')}} myObj ['method1'](); // 1 myObj ['method2'](); // 2
sminutoli 2018年

1
これはゴールドです!!! これまで考えたことがないことに驚きます。いいね!!!
thxmike

また、これは私たちの目標を達成するための最も近い方法だと思います。
Chris Jung、

24

2つのこと:

  • 評価を避けてください、それはひどく危険で遅いです

  • 第二に、あなたの関数がどこに存在するかは関係ありません。x.y.foo()有効にすることができるx.y['foo']()x['y']['foo']()、あるいはwindow['x']['y']['foo']()。このように無制限にチェーンできます。


1
しかし、window ['xyz']()を実行してxyz()を呼び出すことはできません
nickf

17

すべての回答は、関数がグローバルスコープ(ウィンドウ)からアクセスできることを前提としています。ただし、OPはこの仮定を行いませんでした。

関数がローカルスコープ(別名クロージャ)に存在し、他のローカルオブジェクトによって参照されていない場合は、残念ですeval()。AFAIK を使用する必要があります。JavaScriptでローカル関数動的に呼び出すを参照してください


2
おい(またはdudette)、それを指摘してくれてありがとう!私は一瞬頭がおかしくなっていると思った。
Funktr0n 2014年

13

で文字列をポインタに変換するだけですwindow[<method name>]。例:

var function_name = "string";
function_name = window[function_name];

そして今、あなたはそれをポインタのように使うことができます。


これははるかに安全な方法のようです。
James Poulose 2016年

12

Jason BuntingとAlex Nazarovの優れた回答に対する私の貢献を以下に示します。ここには、Crashalotから要求されたエラーチェックが含まれています。

この(考案された)前文を考えると:

a = function( args ) {
    console.log( 'global func passed:' );
    for( var i = 0; i < arguments.length; i++ ) {
        console.log( '-> ' + arguments[ i ] );
    }
};
ns = {};
ns.a = function( args ) {
    console.log( 'namespace func passed:' );
    for( var i = 0; i < arguments.length; i++ ) {
        console.log( '-> ' + arguments[ i ] ); 
    }
};
name = 'nsa';
n_s_a = [ 'Snowden' ];
noSuchAgency = function(){};

次の関数:

function executeFunctionByName( functionName, context /*, args */ ) {
    var args, namespaces, func;

    if( typeof functionName === 'undefined' ) { throw 'function name not specified'; }

    if( typeof eval( functionName ) !== 'function' ) { throw functionName + ' is not a function'; }

    if( typeof context !== 'undefined' ) { 
        if( typeof context === 'object' && context instanceof Array === false ) { 
            if( typeof context[ functionName ] !== 'function' ) {
                throw context + '.' + functionName + ' is not a function';
            }
            args = Array.prototype.slice.call( arguments, 2 );

        } else {
            args = Array.prototype.slice.call( arguments, 1 );
            context = window;
        }

    } else {
        context = window;
    }

    namespaces = functionName.split( "." );
    func = namespaces.pop();

    for( var i = 0; i < namespaces.length; i++ ) {
        context = context[ namespaces[ i ] ];
    }

    return context[ func ].apply( context, args );
}

引数(Arrayオブジェクトを含む)の有無にかかわらず、名前空間またはグローバルの文字列に格納された名前でJavaScript関数を呼び出すことができ、発生したエラー(できればそれらをキャッチ)に関するフィードバックを提供します。

サンプル出力は、それがどのように機能するかを示しています。

// calling a global function without parms
executeFunctionByName( 'a' );
  /* OUTPUT:
  global func passed:
  */

// calling a global function passing a number (with implicit window context)
executeFunctionByName( 'a', 123 );
  /* OUTPUT:
  global func passed:
  -> 123
  */

// calling a namespaced function without parms
executeFunctionByName( 'ns.a' );
  /* OUTPUT:
  namespace func passed:
  */

// calling a namespaced function passing a string literal
executeFunctionByName( 'ns.a', 'No Such Agency!' );
  /* OUTPUT:
  namespace func passed:
  -> No Such Agency!
  */

// calling a namespaced function, with explicit context as separate arg, passing a string literal and array 
executeFunctionByName( 'a', ns, 'No Such Agency!', [ 007, 'is the man' ] );
  /* OUTPUT:
  namespace func passed:
  -> No Such Agency!
  -> 7,is the man
  */

// calling a global function passing a string variable (with implicit window context)
executeFunctionByName( 'a', name );
  /* OUTPUT:
  global func passed:
  -> nsa
  */

// calling a non-existing function via string literal
executeFunctionByName( 'n_s_a' );
  /* OUTPUT:
  Uncaught n_s_a is not a function
  */

// calling a non-existing function by string variable
executeFunctionByName( n_s_a );
  /* OUTPUT:
  Uncaught Snowden is not a function
  */

// calling an existing function with the wrong namespace reference
executeFunctionByName( 'a', {} );
  /* OUTPUT:
  Uncaught [object Object].a is not a function
  */

// calling no function
executeFunctionByName();
  /* OUTPUT:
  Uncaught function name not specified
  */

// calling by empty string
executeFunctionByName( '' );
  /* OUTPUT:
  Uncaught  is not a function
  */

// calling an existing global function with a namespace reference
executeFunctionByName( 'noSuchAgency', ns );
  /* OUTPUT:
  Uncaught [object Object].noSuchAgency is not a function
  */

Dunno ...それは非常に良い努力だ、それは明らかだ。しかし、私には「広すぎる」ように聞こえます...
TechNyquist 2017年

2
えっ?SOは質問/回答/教育プラットフォームです。うまくいけば照明を伝えるために考えられるすべての例を喜んで提供します。私にとっては、それがポイントです。
Mac

とにかくfunctionNameを評価している場合は、それを使用しないのはなぜですか?
データ

これは私にはうまくいきません。名前空間付きの関数abcdがあり、dは関数名です。executeFunctionByName( "abcd"、window)の呼び出しはif( typeof context[ functionName ] !== 'function' )、コンテキスト-ウィンドウ-が定義されており、オブジェクトと配列であるためチェックする行で失敗しますが、ウィンドウ['abcd']は存在しません。回答: window["My.Namespace.functionName"](arguments); // fail
akousmata

12

あなたがいる場所に応じて、あなたも使うことができます:

this["funcname"]();
self["funcname"]();
window["funcname"]();
top["funcname"]();
globalThis["funcname"]();

または、nodejs

global["funcname"]()

9

でグローバル関数の代わりにオブジェクトの関数を呼び出したい場合window["functionName"]。あなたはそれを好きにできます。

var myObject=new Object();
myObject["functionName"](arguments);

例:

var now=new Date();
now["getFullYear"]()

8

注意してください!!!

次の2つの理由により、JavaScriptで文字列によって関数を呼び出さないようにする必要があります。

理由1:一部のコード難読化ツールは、関数名を変更して文字列を無効にするため、コードを破壊します。

理由2:文字列によって呼び出されるメソッドの使用状況を特定するのが非常に難しいため、この方法を使用するコードを維持することははるかに困難です。


7

ここに、Es6のアプローチを示します。これにより、文字列としての名前または関数名で関数を呼び出すことができ、さまざまなタイプの関数にさまざまな数の引数を渡すことができます。

function fnCall(fn, ...args)
{
  let func = (typeof fn =="string")?window[fn]:fn;
  if (typeof func == "function") func(...args);
  else throw new Error(`${fn} is Not a function!`);
}


function example1(arg1){console.log(arg1)}
function example2(arg1, arg2){console.log(arg1 + "  and   " + arg2)}
function example3(){console.log("No arguments!")}

fnCall("example1", "test_1");
fnCall("example2", "test_2", "test3");
fnCall(example3);
fnCall("example4"); // should raise an error in console


6

setTimeoutの記述がないことに驚いています。

引数なしで関数を実行するには:

var functionWithoutArguments = function(){
    console.log("Executing functionWithoutArguments");
}
setTimeout("functionWithoutArguments()", 0);

引数を指定して関数を実行するには:

var functionWithArguments = function(arg1, arg2) {
    console.log("Executing functionWithArguments", arg1, arg2);
}
setTimeout("functionWithArguments(10, 20)");

名前空間の深い関数を実行するには:

var _very = {
    _deeply: {
        _defined: {
            _function: function(num1, num2) {
                console.log("Execution _very _deeply _defined _function : ", num1, num2);
            }
        }
    }
}
setTimeout("_very._deeply._defined._function(40,50)", 0);

これは質問に対する答えを提供しません。批評したり、著者に説明を求めたりするには、投稿の下にコメントを残します。自分の投稿にはいつでもコメントできます。十分な評判得られれ、どの投稿にもコメントできます。
AstroCB 2014年

runMeいくつかの引数を使用して呼び出す方法の例を追加してください。
Lexicore 2014年

1
@lexicore 質問に対する実質的な回答が明確に提供されておらず、それ自体ではほとんど価値がないため、レビューキューでの削除に投票しました。
AstroCB 2014年

1
このメソッドは実行をレンダリングキューの最後に置くため、この呼び出しを非同期にする
PeterM

1
私はこの答えが好きです、それは私の要件に合うようです。
クイントン2018

3

だから、他の人が言ったように、間違いなく最良の選択肢は:

window['myfunction'](arguments)

そして、Jason Buntingが言ったように、関数の名前にオブジェクトが含まれている場合は機能しません。

window['myobject.myfunction'](arguments); // won't work
window['myobject']['myfunction'](arguments); // will work

だから、これは名前ですべての関数を実行する関数の私のバージョンです(オブジェクトを含むかどうかに関係なく):

my = {
    code : {
        is : {
            nice : function(a, b){ alert(a + "," + b); }
        }
    }
};

guy = function(){ alert('awesome'); }

function executeFunctionByName(str, args)
{
    var arr = str.split('.');
    var fn = window[ arr[0] ];
    
    for (var i = 1; i < arr.length; i++)
    { fn = fn[ arr[i] ]; }
    fn.apply(window, args);
}

executeFunctionByName('my.code.is.nice', ['arg1', 'arg2']);
executeFunctionByName('guy');


3
  let t0 = () => { alert('red0') }
  var t1 = () =>{ alert('red1') }
  var t2 = () =>{ alert('red2') }
  var t3 = () =>{ alert('red3') }
  var t4 = () =>{ alert('red4') }
  var t5 = () =>{ alert('red5') }
  var t6 = () =>{ alert('red6') }

  function getSelection(type) {
    var evalSelection = {
      'title0': t0,
      'title1': t1,
      'title2': t2,
      'title3': t3,
      'title4': t4,
      'title5': t5,
      'title6': t6,
      'default': function() {
        return 'Default';
      }
    };
    return (evalSelection[type] || evalSelection['default'])();
  }
  getSelection('title1');

より多くのOOPソリューション...


2

JasonとAlexの投稿に関するもう1つの詳細。コンテキストにデフォルト値を追加すると便利です。context = context == undefined? window:context;関数の最初に置くだけです。window好みのコンテキストに変更することができ、デフォルトのコンテキストでこれを呼び出すたびに同じ変数を渡す必要はありません。


2

Jason Buntingの答えに追加するには、nodejsまたは何かを使用している場合(これはdom jsでも機能します)、this代わりに使用できますwindow(そして、覚えておいてください:eval is evil

this['fun'+'ctionName']();

2

私のコードには非常によく似たものがあります。サードパーティライブラリのコールバックとして渡す必要がある関数名を含むサーバー生成の文字列があります。だから私は文字列を取り、関数への「ポインタ」を返すコードがあり、見つからない場合はnullです。

私の解決策は「Jason Buntingの非常に役立つ機能*に非常に似ていましたが、自動実行されず、コンテキストは常にウィンドウ上にあります。ただし、これは簡単に変更できます。

うまくいけば、これは誰かに役立つでしょう。

/**
 * Converts a string containing a function or object method name to a function pointer.
 * @param  string   func
 * @return function
 */
function getFuncFromString(func) {
    // if already a function, return
    if (typeof func === 'function') return func;

    // if string, try to find function or method of object (of "obj.func" format)
    if (typeof func === 'string') {
        if (!func.length) return null;
        var target = window;
        var func = func.split('.');
        while (func.length) {
            var ns = func.shift();
            if (typeof target[ns] === 'undefined') return null;
            target = target[ns];
        }
        if (typeof target === 'function') return target;
    }

    // return null if could not parse
    return null;
}


1

関数名を含む文字列の一部として渡されている引数の数が不明な場合役立つ、別のトリックについて言及するのは仕方ありません。例えば:

var annoyingstring = 'call_my_func(123, true, "blah")';

JavaScriptがHTMLページで実行されている場合、必要なのは目に見えないリンクだけです。文字列をonclick属性に渡し、clickメソッドを呼び出すことができます。

<a href="#" id="link_secret"><!-- invisible --></a>

$('#link_secret').attr('onclick', annoyingstring);
$('#link_secret').click();

または<a>、実行時に要素を作成します。


クリエイティブなソリューションですが、これはオブジェクトまたは配列型の引数には機能しません。
Dennis Heiden 2016年

1
これはフードの下でevalを使用しています...そして実際にそれを行うために茂みの周りを叩いています
フアンメンデス

1

最も簡単な方法は、要素があるようにそれにアクセスすることです

window.ClientSideValidations.forms.location_form

と同じ

window.ClientSideValidations.forms['location_form']

1

eval("functionname as string")どちらでもJavaScript関数を呼び出すことができます。以下のように:(evalは純粋なJavaScript関数です)

function testfunc(){
    return "hello world";
}

$( document ).ready(function() {

     $("div").html(eval("testfunc"));
});

作業例:https : //jsfiddle.net/suatatan/24ms0fna/4/


この作品罰金とそののでシンプル
カルロス・E

1
そしてまた本当に遅い。
マルコ

1

これは私のために働いています:

var command = "Add";
var tempFunction = new Function("Arg1","Arg2", "window." + command + "(Arg1,Arg2)");
tempFunction(x,y);

これがうまくいくことを願っています。


1

複雑な中間関数やevalが必要だったり、ウィンドウのようなグローバル変数に依存しているとは思いません。

function fun1(arg) {
  console.log(arg);
}

function fun2(arg) {
  console.log(arg);
}

const operations = {
  fun1,
  fun2
};

let temp = "fun1";

try {
  // You have to use square brackets property access
  operations["fun1"]("Hello World");
  operations["fun2"]("Hello World");
  // You can use variables
  operations[temp]("Hello World");
} catch (error) {
  console.error(error);
}

インポートされた関数でも動作します:

// mode.js
export function fun1(arg) {
  console.log(arg);
}

export function fun2(arg) {
  console.log(arg);
}
// index.js
import { fun1, fun2 } from "./mod";

const operations = {
  fun1,
  fun2
};

try {
  operations["fun1"]("Hello World");
  operations["fun2"]("Hello World");
} catch (error) {
  console.error(error);
}

0

を使用しeval('function()')ない場合は、を使用して新しい関数を作成できますnew Function(strName)。以下のコードは、FF、Chrome、IEを使用してテストされました。

<html>
<body>
<button onclick="test()">Try it</button>
</body>
</html>
<script type="text/javascript">

  function test() {
    try {    
        var fnName = "myFunction()";
        var fn = new Function(fnName);
        fn();
      } catch (err) {
        console.log("error:"+err.message);
      }
  }

  function myFunction() {
    console.log('Executing myFunction()');
  }

</script>

0
use this

function executeFunctionByName(functionName, context /*, args */) {
      var args = [].slice.call(arguments).splice(2);
      var namespaces = functionName.split(".");
      var func = namespaces.pop();
      for(var i = 0; i < namespaces.length; i++) {
        context = context[namespaces[i]];
      }
      return context[func].apply(context, args);
    }

1
どうして?説明のない答えはほとんどの場合役に立たないでしょう。
ダニエルW.

0

基本的に見て:

var namefunction = 'jspure'; // String

function jspure(msg1 = '', msg2 = '') { 
  console.log(msg1+(msg2!=''?'/'+msg2:''));
} // multiple argument

// Results ur test
window[namefunction]('hello','hello again'); // something...
eval[namefunction] = 'hello'; // use string or something, but its eval just one argument and not exist multiple

他の型の関数はクラスであり、例はnil petersohnです


0

非常に役立つ回答をありがとう。私のプロジェクトではJason Buntingの機能を使用しています。

タイムアウトを設定する通常の方法は機能しないため、オプションのタイムアウトを使用するように拡張しました。abhishekisnotさんの質問を参照してください

function executeFunctionByName(functionName, context, timeout /*, args */ ) {
	var args = Array.prototype.slice.call(arguments, 3);
	var namespaces = functionName.split(".");
	var func = namespaces.pop();
	for (var i = 0; i < namespaces.length; i++) {
		context = context[namespaces[i]];
	}
	var timeoutID = setTimeout(
		function(){ context[func].apply(context, args)},
		timeout
	);
    return timeoutID;
}

var _very = {
    _deeply: {
        _defined: {
            _function: function(num1, num2) {
                console.log("Execution _very _deeply _defined _function : ", num1, num2);
            }
        }
    }
}

console.log('now wait')
executeFunctionByName("_very._deeply._defined._function", window, 2000, 40, 50 );

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.