이름이 문자열 일 때 JavaScript 함수를 실행하는 방법
JavaScript의 함수 이름이 문자열로 있습니다. 나중에 호출 할 수 있도록 함수 포인터로 어떻게 변환합니까?
상황에 따라 다양한 인수를 메서드에 전달해야 할 수도 있습니다.
일부 기능은 namespace.namespace.function(args[...])
.
절대적으로 다른 선택이 없다면 사용 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);
Jason Bunting의 매우 유용한 기능을 약간 변경 한 버전을 게시 할 것이라고 생각했습니다 .
먼저 slice ()에 두 번째 매개 변수를 제공하여 첫 번째 명령문을 단순화했습니다 . 원래 버전은 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);
}
이 다른 질문에 대한 대답은 그 방법을 보여줍니다. Javascript가 Python의 locals ()에 해당합니까?
기본적으로 다음과 같이 말할 수 있습니다.
window["foo"](arg1, arg2);
또는 다른 많은 사람들이 제안했듯이 eval을 사용할 수 있습니다.
eval(fname)(arg1, arg2);
당신이 평가하는 것에 대해 절대적으로 확신하지 않는 한 이것은 매우 안전하지 않습니다.
당신은 이것을 할 수 없습니다 :
var codeToExecute = "My.Namespace.functionName()";
var tmpFunc = new Function(codeToExecute);
tmpFunc();
이 메서드를 사용하여 다른 JavaScript를 실행할 수도 있습니다.
이를 수행하는 우아한 방법은 해시 객체에서 함수를 정의하는 것입니다. 그런 다음 문자열을 사용하여 해시에서 해당 함수에 대한 참조를 가질 수 있습니다. 예 :
var customObject = {
customFunction: function(param){...}
};
그런 다음 전화 할 수 있습니다.
customObject['customFunction'](param);
여기서 customFunction은 객체에 정의 된 함수와 일치하는 문자열입니다.
ES6를 사용하면 이름으로 클래스 메서드에 액세스 할 수 있습니다.
class X {
method1(){
console.log("1");
}
method2(){
this['method1']();
console.log("2");
}
}
let x = new X();
x['method2']();
출력은 다음과 같습니다.
1
2
두가지:
평가를 피하십시오. 매우 위험하고 느립니다.
둘째, 함수가 어디에 있는지는 중요하지 않습니다. "글로벌"은 무관합니다.
x.y.foo()
를 통해 사용할 수 있습니다x.y['foo']()
또는x['y']['foo']()
심지어window['x']['y']['foo']()
. 이와 같이 무기한으로 연결할 수 있습니다.
모든 답변은 전역 범위 (일명 창)를 통해 함수에 액세스 할 수 있다고 가정합니다. 그러나 OP는 이러한 가정을하지 않았습니다.
함수가 로컬 범위 (일명 클로저)에 있고 다른 로컬 객체에서 참조되지 않는 경우, 운이 좋지 않습니다. eval () AFAIK 를 사용해야 합니다. 자바 스크립트에서 동적으로 로컬 함수 호출을 참조하세요.
당신은 당신의 문자열을 포인터로 변환해야합니다 window[<method name>]
. 예:
var function_name = "string";
function_name = window[function_name];
이제 포인터처럼 사용할 수 있습니다.
다음은 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 );
}
인수 (배열 객체 포함)를 포함하거나 포함하지 않고 네임 스페이스 또는 전역 문자열에 저장된 이름으로 자바 스크립트 함수를 호출하여 발생한 오류에 대한 피드백을 제공 할 수 있습니다.
샘플 출력은 작동 방식을 보여줍니다.
// 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
*/
을 사용하여 전역 함수 대신 객체의 함수를 호출하려는 경우 window["functionName"]
. 당신은 그것을 할 수 있습니다;
var myObject=new Object();
myObject["functionName"](arguments);
예:
var now=new Date();
now["getFullYear"]()
조심해!!!
다음 두 가지 이유로 JavaScript에서 문자열로 함수를 호출하지 않도록해야합니다.
이유 1 : 일부 코드 난독 화자는 함수 이름을 변경하여 문자열을 유효하지 않게 만들기 때문에 코드를 손상시킵니다.
이유 2 :이 방법론을 사용하는 코드를 유지하는 것이 훨씬 더 어렵습니다. 문자열에 의해 호출 된 메서드의 사용법을 찾는 것이 훨씬 더 어렵 기 때문입니다.
현재 위치에 따라 다음을 사용할 수도 있습니다.
this["funcname"]();
self["funcname"]();
window["funcname"]();
top["funcname"]();
globalThis["funcname"]();
또는 nodejs에서
global["funcname"]()
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);
다음은 이름으로 함수를 문자열 또는 함수 이름으로 호출하고 다른 유형의 함수에 다른 개수의 인수를 전달할 수있게 해주는 내 Es6 접근 방식입니다.
function fnCall(fn, ...args)
{
let func = (typeof fn =="string")?window[fn]:fn;
if (typeof func == "function") func(...args)
else console.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
따라서 다른 사람들이 말했듯이 가장 좋은 옵션은 다음과 같습니다.
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');
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 솔루션 ...
Jason과 Alex의 게시물에 대한 추가 정보. 컨텍스트에 기본값을 추가하는 것이 도움이된다는 것을 알았습니다. context = context == undefined? window:context;
함수의 시작 부분에 넣으십시오 . window
선호하는 컨텍스트로 변경할 수 있으며 기본 컨텍스트에서이를 호출 할 때마다 동일한 변수를 전달할 필요가 없습니다.
내 코드에는 매우 유사한 것이 있습니다. 타사 라이브러리에 대한 콜백으로 전달해야하는 함수 이름이 포함 된 서버 생성 문자열이 있습니다. 그래서 나는 문자열을 받아서 함수에 대한 "포인터"를 반환하는 코드를 가지고 있습니다. 발견되지 않으면 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;
}
매우 유용한 방법이 너무 있습니다.
http://devlicio.us/blogs/sergio_pereira/archive/2009/02/09/javascript-5-ways-to-call-a-function.aspx
var arrayMaker = {
someProperty: 'some value here',
make: function (arg1, arg2) {
return [ this, arg1, arg2 ];
},
execute: function_name
};
Jason Bunting의 답변에 추가하려면 nodejs 또는 무언가를 사용하는 경우 (그리고 이것은 dom js에서도 작동합니다) this
대신 사용할 수 있습니다 window
(그리고 기억하십시오 : eval is evil :
this['fun'+'ctionName']();
함수 이름을 포함하는 문자열의 일부로 전달되는 알 수없는 수의 인수가있는 경우 도움이 되는 또 다른 트릭을 언급하지 않을 수 없습니다 . 예를 들면 :
var annoyingstring = 'call_my_func(123, true, "blah")';
자바 스크립트가 HTML 페이지에서 실행되고 있다면 보이지 않는 링크 만 있으면됩니다. onclick
속성에 문자열을 전달 하고 click
메서드를 호출 할 수 있습니다 .
<a href="#" id="link_secret"><!-- invisible --></a>
$('#link_secret').attr('onclick', annoyingstring);
$('#link_secret').click();
또는 <a>
런타임에 요소를 만듭니다 .
가장 쉬운 방법은 요소가있는 것처럼 액세스하는 것입니다.
window.ClientSideValidations.forms.location_form
다음과 같다
window.ClientSideValidations.forms['location_form']
사용하지 않고 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>
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);
}
기본보기 :
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
존재하는 다른 유형의 함수는 클래스 이며 예제 nils petersohn
매우 유용한 답변에 감사드립니다. 내가 사용하고 제이슨 멧새의 기능을 내 프로젝트에.
제한 시간을 설정하는 일반적인 방법이 작동하지 않기 때문에 선택적 제한 시간과 함께 사용하도록 확장했습니다. 참조 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 );
executeByName
이름에 대괄호 가 포함되어 있지 않으면 잘 작동하는 몇 가지 기능이 있습니다 . 따라서 위의 기능은 다음과 같은 이름에서 실패합니다.
app.widget['872LfCHc']['toggleFolders']
구제책으로, 나는 이것을 고려하는 기능을 만들었습니다. 아마도 누군가가 유용하다고 생각할 것입니다.
CoffeeScript에서 생성 :
var executeByName = function(name, context) {
var args, func, i, j, k, len, len1, n, normalizedName, ns;
if (context == null) {
context = window;
}
args = Array.prototype.slice.call(arguments, 2);
normalizedName = name.replace(/[\]'"]/g, '').replace(/\[/g, '.');
ns = normalizedName.split(".");
func = context;
for (i = j = 0, len = ns.length; j < len; i = ++j) {
n = ns[i];
func = func[n];
}
ns.pop();
for (i = k = 0, len1 = ns.length; k < len1; i = ++k) {
n = ns[i];
context = context[n];
}
if (typeof func !== 'function') {
throw new TypeError('Cannot execute function ' + name);
}
return func.apply(context, args);
}
가독성을 높이려면 CoffeeScript 버전도 확인하십시오.
executeByName = (name, context = window) ->
args = Array.prototype.slice.call(arguments, 2)
normalizedName = name.replace(/[\]'"]/g, '').replace(/\[/g, '.')
ns = normalizedName.split "."
func = context
for n, i in ns
func = func[n]
ns.pop()
for n, i in ns
context = context[n];
if typeof func != 'function'
throw new TypeError 'Cannot execute function ' + name
func.apply(context, args)
eval("functionname as string")
둘 중 하나 에서 javascript 함수를 호출 할 수 있습니다 . 아래와 같이 : (eval은 순수 자바 스크립트 함수입니다)
function testfunc(){
return "hello world";
}
$( document ).ready(function() {
$("div").html(eval("testfunc"));
});
작동 예 : https://jsfiddle.net/suatatan/24ms0fna/4/
컨텍스트를 사용하거나 함수가 상주하는 새로운 컨텍스트를 정의하기 만하면됩니다. 당신은 제한되지 않습니다window["f"]();
다음은 일부 REST 서비스에 대한 동적 호출을 사용하는 방법의 예입니다.
/*
Author: Hugo Reyes
@ www.teamsrunner.com
*/
(function ( W, D) { // enclose it as self invoking function to avoid name collisions.
// to call function1 as string
// initialize your FunctionHUB as your namespace - context
// you can use W["functionX"](), if you want to call a function at the window scope.
var container = new FunctionHUB();
// call a function1 by name with one parameter.
container["function1"](' Hugo ');
// call a function2 by name.
container["function2"](' Hugo Leon');
// OO style class
function FunctionHUB() {
this.function1 = function (name) {
console.log('Hi ' + name + ' inside function 1')
}
this.function2 = function (name) {
console.log('Hi' + name + ' inside function 2 ')
}
}
})(window, document); // in case you need window context inside your namespace.
문자열에서 전체 함수를 생성하려면 다른 대답입니다. 또한 이름 공간 my.name.space.for.functions.etc.etc.etc
의 마지막 분기에 다음과 같은 기능이 포함되어 있으므로 이름 공간이 존재하는 경우 단일 이름 공간으로 제한되지 않습니다.my.name.space.for.functions.etc.etc["function"]();
도움이되기를 바랍니다. H.
'Development Tip' 카테고리의 다른 글
문자열에서 switch 문을 사용할 수없는 이유는 무엇입니까? (0) | 2020.09.27 |
---|---|
이상한 언어 기능 (0) | 2020.09.27 |
jQuery를 사용하여 div의 innerHTML을 대체하는 방법은 무엇입니까? (0) | 2020.09.27 |
CSS로 div의 종횡비 유지 (0) | 2020.09.27 |
Ruby에서 attr_accessor는 무엇입니까? (0) | 2020.09.27 |