2010-12-03 8 views
26

Je veux être en mesure de vérifier si une fonction donnée est vide ou non. Autrement dit, il n'y a rien dans son corps, par exemple:Comment déterminer si une fonction est vide

function foo() {} 
function iAmEmpty(a) { 
    // yep, empty 
} 

Avec un peu de jeu initial autour, j'ai quelque chose que je pense peut-être ok, en utilisant toString() et quelques expressions rationnelles.

function foo(a, b, c) {} 

/^function[^{]+\{\s*\}/m.test(foo.toString()); // true 

function bar(a, b, c) { var d; } 

/^function[^{]+\{\s*\}/m.test(bar.toString()); // false 

Je me demandais juste s'il y avait une meilleure approche? Y at-il des problèmes avec ce qui précède, vous pouvez le voir?

+0

question de Nice ... –

+1

Le concept de réflexion JS m'a fait lol, mais comme le problème; vous avez traité avec des espaces et des multilignes, donc c'est aussi bon que ça. – annakata

+1

N'oubliez pas de rechercher les corps de méthode qui sont tous les commentaires (si c'est votre spécification) – Brad

Répondre

15

Ceci n'est pas recommandé. Il n'y a pas de standard déterminant précisément la méthode que la méthode toString() devrait renvoyer, donc même si cela fonctionne dans les navigateurs actuels, les futurs navigateurs peuvent légitimement changer leur implémentation et casser votre code.

Kangax a écrit brièvement ceci: http://perfectionkills.com/those-tricky-functions/

+0

Merci pour le lien! Je n'ai pas réalisé que les différences dans les implémentations de navigateurs étaient si extrêmes. – nickf

+0

@Tim ES5: [Prototype de la fonction.toString] (http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.4.2) ES6 [Function.prototype.toString] (https://www.ecma-international.org /ecma-262/6.0/#sec-function.prototype.tostring) –

+0

@ManasJayanth: Le mot clé est "justement". Les deux spécifications indiquent qu'une "représentation dépendante de l'implémentation" de la fonction est renvoyée. –

2

Je ne vois pas l'utilité pour cela, mais vous pouvez le rendre plus simple en ancrant le motif à la fin de la chaîne.

/[^{\s]\s*\}$/.test(String(bar)) 
5

La meilleure chose que vous pouvez essayer, pour adapter les possibilités maximales (car il est assez difficile à réaliser), est d'ajouter acorn ou esprima (fonctionne avec des fonctions de direction trop) les bibliothèques et le processus de la fonction JavaScript . Il va l'analyser pour que vous puissiez l'analyser, afin de vérifier s'il y a zéro code à l'intérieur, ou s'il n'y a que des déclarations variables sans calcul ni retour, etc ...

Est assez simple à implémenter :

function check(f) { 
 
    console.log(""); 
 
    console.log("Checking", f); 
 
    var syntax = esprima.parse(f); 
 
    if (syntax.body.length != 1) { 
 
    console.log("Weird process"); 
 
    } else { 
 
    function processBody(body) { 
 
     if (!body || body.length == 0) { 
 
     console.log("Body seems empty. YAY!"); 
 
     } else { 
 
     for (var i = 0, command; command = body[i]; i++) { 
 
      if (command.type != "VariableDeclaration") { 
 
      console.log("Body has a", command.type, ", so is not empty."); 
 
      return; 
 
      } 
 
     } 
 
     console.log("Body has only variable declarations, so is empty! (or not, whatever fit your needs)"); 
 
     } 
 
    } 
 
    function process(dec) { 
 
     if (dec.type != "FunctionDeclaration") { 
 
     console.log("Weird declaration"); 
 
     } else { 
 
     console.log("Function", dec.id.name, "has", dec.params.length, "params"); 
 
     processBody(dec.body.body); 
 
     } 
 
    } 
 
    process(syntax.body[0]); 
 
    } 
 
} 
 

 
check("function hasReturn(arg1, arg2) {var a = arg1 + arg2;return a;}"); 
 
check("function hasArgumentsButNoBody(arg1, arg2) {}"); 
 
check("function isCompletelyEmptyWithSpacesAndTabsAndLinefeeds() { \t \t \r\n \r\n }"); 
 
check("function hasOnlyVariables() {var a, b = 2; var c = 1 + b;}");
<script src="https://cdnjs.cloudflare.com/ajax/libs/esprima/2.7.3/esprima.min.js"></script>

Cela ne fonctionnera pas les fonctions, il suffit de les analyser, donc est sûr de fonctionner avec des fonctions non sécurisées.

7

fonctions Arrow ...

Comme je suis sûr que vous savez, javascript soutient arrow functions qui sont vraiment succinct, mais, malheureusement, ne fonctionne pas avec votre propre regex.

J'ai rapidement converti votre joli regex en son propre function qui prend comme function une entrée et retourne si elle est vide par souci de simplicité plus tard. Juste pour montrer comment les fonctions de direction peuvent être largement utilisés, je l'ai mis dans un:

isEmpty = f => /^function[^{]+\{\s*\}/m.test(f.toString()) 

Maintenant, nous pouvons facilement tester une fonction vide:

function eF() {} 

qui, comme nous attendre avec isEmpty(eF) rendements true .

Et une fois de plus avec une fonction réelle:

function retOne() {return 1;} 

qui encore une fois comme prévu avec des rendements isEmpty(retOne)false.

Cependant, la question que j'ai rencontré des fonctions de direction afin d'initialiser un vide une fois, nous avons une syntaxe plus courte de l'original:

eF =() => {} 

et la version 'stringified' cela est tout à fait différent de celui avant:

"() => {}" 

donc bien sûr dans ce cas, l'appel isEmpty(eF) retours false quand nous voulons true. Je ne suis pas sûr si vous avez besoin de tester si toutes les fonctions (y compris arrow functions) sont vides, mais si vous le faites, votre regex devra être modifié ...

Je ne suis pas génial à les écrire moi-même, mais j'ai essayé un couple et une autre chose que vous voudrez peut-être à considérer est la nature clémente du arrow functions surtout cette partie du documentation:

(param1, param2, …, paramN) => { statements } 
(param1, param2, …, paramN) => expression 
// equivalent to: (param1, param2, …, paramN) => { return expression; } 

// Parentheses are optional when there's only one parameter name: 
(singleParam) => { statements } 
singleParam => { statements } 

qui montre comment les accolades {...} ne sont pas toujours nécessaires. Donc, cette fonction:

retOne =() => 1 

est valid et pourrait faire former un nouveau regex plus difficile. Une solution de contournement que je pensais est de simplement supprimer tous les accolades de f.toString() en utilisant:

str.replace(/[{}]/g, '').

puis de travailler avec un regex test à partir de là.

Espérons que cela soit utile si vous voulez que arrow functions puisse également être testé.

1

Il suffit de vérifier la fonction contient puis vérifiez le contenu s'il est vide ou non.
vérifier ce Plunker
Code de travail est complet ici:

function foo(a, b, c) {} 

function bar(a, b, c) { 
    var d; 
} 

function isEmpty(value) { 
    return (value == null || value.length === 0); 
} 

function check(test) { 
    var entire = test.toString(); 
    var body = entire.slice(entire.indexOf("{") + 1, entire.lastIndexOf("}")); 
    return body; 

} 
console.log(isEmpty(check(foo)), isEmpty(check(bar))); //return true false 
+0

Cela ne fonctionne pas si la fonction remplace la méthode toString. –

+0

@AlexisTyler vérifier le plunker –

+0

@AlexisTyler il y a une mention à propos de l'overriding dans votre question de toute façon vous pouvez sauvegarder la fonction toString par défaut dans une autre fonction si vous souhaitez la surcharger –

2
Function.prototype.hasBody = function() { 
    return !/{\s*}$/.test(this.toString()); 
}; 
Questions connexes