web-dev-qa-db-fra.com

Comment transformer une chaîne en un appel de fonction JavaScript?

J'ai une chaîne comme:

settings.functionName + '(' + t.parentNode.id + ')';

que je veux traduire en appel de fonction comme ceci:

clickedOnItem(IdofParent);

Ceci devra bien sûr être fait en JavaScript. Lorsque je fais une alerte sur settings.functionName + '(' + t.parentNode.id + ')';, tout semble correct. J'ai juste besoin d'appeler la fonction dans laquelle elle se traduirait.

Légende:

settings.functionName = clickedOnItem

t.parentNode.id = IdofParent
254

Voir que je déteste eval et que je suis pas seul :

var fn = window[settings.functionName];
if(typeof fn === 'function') {
    fn(t.parentNode.id);
}

Edit: En réponse au commentaire de @ Mahan: Dans ce cas particulier, settings.functionName serait "clickedOnItem". Lors de l'exécution, cela traduirait var fn = window[settings.functionName]; en var fn = window["clickedOnItem"], ce qui donnerait une référence à function clickedOnItem (nodeId) {}. Une fois que nous avons une référence à une fonction dans une variable, nous pouvons appeler cette fonction en "appelant la variable", c’est-à-dire fn(t.parentNode.id), ce qui équivaut à clickedOnItem(t.parentNode.id), comme le voulait le PO.

Exemple plus complet:

/* Somewhere: */
window.settings = {
  /* [..] Other settings */
  functionName: 'clickedOnItem'
  /* , [..] More settings */
};

/* Later */
function clickedOnItem (nodeId) {
  /* Some cool event handling code here */
}

/* Even later */
var fn = window[settings.functionName]; 
/* note that settings.functionName could also be written
   as window.settings.functionName. In this case, we use the fact that window
   is the implied scope of global variables. */
if(typeof fn === 'function') {
    fn(t.parentNode.id);
}
356
PatrikAkerstrand
window[settings.functionName](t.parentNode.id);

Pas besoin d'un eval ()

71
Fabien Ménager

Voici une manière plus générique de faire la même chose, tout en prenant en charge les portées:

// Get function from string, with or without scopes (by Nicolas Gauthier)
window.getFunctionFromString = function(string)
{
    var scope = window;
    var scopeSplit = string.split('.');
    for (i = 0; i < scopeSplit.length - 1; i++)
    {
        scope = scope[scopeSplit[i]];

        if (scope == undefined) return;
    }

    return scope[scopeSplit[scopeSplit.length - 1]];
}

J'espère que cela peut aider certaines personnes.

67
NGauthier

JavaScript a une fonction eval qui évalue une chaîne et l'exécute en tant que code:

eval(settings.functionName + '(' + t.parentNode.id + ')');
14
Andrew Hare

eval () est la fonction dont vous avez besoin pour faire cela, mais je vous conseillerais d'essayer l'une de ces choses pour minimiser l'utilisation de eval. J'espère que l'un d'eux vous semblera logique.

Stocke la fonction

Stockez la fonction en tant que fonction, pas en tant que chaîne, et utilisez-la ultérieurement. Où vous stockez réellement la fonction est à vous.

var funcForLater = clickedOnItem;

// later is now
funcForLater(t.parentNode.id);

ou

someObject.funcForLater = clickedOnItem;    
// later is now    
(someObject.funcForLater)(t.parentNode.id);

Nom de la fonction de stockage

Même si vous devez stocker le nom de la fonction sous forme de chaîne, vous pouvez réduire la complexité en procédant comme suit:

(eval(settings.functionName))(t.parentNode.id);

ce qui minimise la quantité de Javascript que vous devez construire et évaluer.

Dictionnaire des gestionnaires

Placez toutes les fonctions d'action dont vous pourriez avoir besoin dans un objet et appelez-les à la manière d'un dictionnaire en utilisant la chaîne.

// global
itemActions = { click: clickedOnItem, rightClick: rightClickedOnItem /* etc */ };

// Later...
var actionName = "click"; // Or wherever you got the action name
var actionToDo = itemActions[actionName];
actionToDo(t.parentNode.id);

(Remarque mineure: si vous utilisiez plutôt la syntaxe itemActions[actionName](t.parentNode.id); alors la fonction serait appelée comme méthode de itemActions.)

13
Jesse Millikan

Bien que j'aime la première réponse et que je déteste eval, j'aimerais ajouter qu'il existe un autre moyen (similaire à eval), donc si vous pouvez le contourner et ne pas l'utiliser, vous feriez mieux de le faire. Mais dans certains cas, vous voudrez peut-être appeler du code javascript avant ou après un appel ajax. Si vous avez ce code dans un attribut personnalisé au lieu de ajax, vous pouvez utiliser ceci:

    var executeBefore = $(el).attr("data-execute-before-ajax");
    if (executeBefore != "") {
        var fn = new Function(executeBefore);
        fn();
    }

Ou éventuellement stocker cela dans un cache de fonctions si vous devez l'appeler plusieurs fois.

Encore une fois - n'utilisez pas eval ou cette méthode si vous avez un autre moyen de le faire.

9
nsimeonov

Si settings.functionName est déjà une fonction, vous pouvez faire ceci:

settings.functionName(t.parentNode.id);

Sinon, cela devrait également fonctionner si settings.functionName n'est que le nom de la fonction:

if (typeof window[settings.functionName] == "function") {
    window[settings.functionName](t.parentNode.id);
}
8
Gumbo

Je voulais pouvoir prendre un nom de fonction en tant que chaîne, l'appeler ET passer un argument à la fonction. Je ne pouvais pas obtenir la réponse choisie pour cette question, mais ceci réponse l'expliquait exactement, et voici une courte démo.

function test_function(argument)    {
    alert('This function ' + argument); 
}

functionName = 'test_function';

window[functionName]('works!');

Cela fonctionne aussi avec plusieurs arguments.

7
B Rad C

Cela m'a pris du temps à comprendre, car la window['someFunctionName']() conventionnelle ne fonctionnait pas pour moi au début. Les noms de mes fonctions ont été extraits en tant que réponse AJAX d'une base de données. Aussi, pour une raison quelconque, mes fonctions ont été déclarées en dehors du champ de la fenêtre, aussi, afin de résoudre ce problème, je devais réécrire les fonctions que j'appelais depuis

function someFunctionName() {}

à

window.someFunctionName = function() {}

et à partir de là, je pourrais facilement appeler window['someFunctionName'](). J'espère que ça aidera quelqu'un!

3
illusivefingers

Basé sur la réponse de Nicolas Gauthier:

var strng = 'someobj.someCallback';
var data = 'someData';

var func = window;
var funcSplit = strng.split('.');
for(i = 0;i < funcSplit.length;i++){
   //We maybe can check typeof and break the bucle if typeof != function
   func = func[funcSplit[i]];
}
func(data);

Je préfère utiliser quelque chose comme ceci:

window.callbackClass['newFunctionName'] = function(data) { console.log(data) };
...
window.callbackClass['newFunctionName'](data);
2
Tom Johnson

En javascript qui utilise la spécification CommonJS, comme node.js par exemple, vous pouvez faire ce que je montre ci-dessous. Ce qui est très pratique pour accéder à une variable par une chaîne, même si ce n'est pas défini sur l'objet window. S'il existe une classe nommée MyClass, définie dans un module CommonJS nommé MyClass.js

// MyClass.js
var MyClass = function() {
    // I do stuff in here. Probably return an object
    return {
       foo: "bar"
    }
}

module.exports = MyClass;

Vous pouvez ensuite faire cela. Un peu de sorcellerie à partir d’un autre fichier appelé MyOtherFile.js

// MyOtherFile.js

var myString = "MyClass";

var MyClass = require('./' + myString);
var obj = new MyClass();

console.log(obj.foo); // returns "bar"

Une raison de plus pour laquelle CommonJS est un tel plaisir.

1
nackjicholson