web-dev-qa-db-fra.com

comment comparer 2 fonctions en javascript

Comment comparer 2 fonctions en javascript? Je ne parle pas de référence interne. Dire 

var a = function(){return 1;};
var b = function(){return 1;};

Est-il possible de comparer a et b?

44
onemach
var a = b = function( c ){ return c; };
//here, you can use a === b because they're pointing to the same memory and they're the same type

var a = function( c ){ return c; },
    b = function( c ){ return c; };
//here you can use that byte-saver Andy E used (which is implicitly converting the function to it's body's text as a String),

''+a == ''+b.

//this is the Gist of what is happening behind the scences:

a.toString( ) == b.toString( )  
50
Rony SP

Les fermetures signifient que vous devez faire très attention à ce que vous voulez dire lorsque vous dites "comparer". Par exemple:

function closure( v ) { return function(){return v} };
a = closure('a'); b = closure('b');
[a(), b()]; // ["a", "b"]

// Now, are a and b the same function?
// In one sense they're the same:
a.toString() === b.toString(); // true
// In another sense they're different:
a() === b(); // false

La capacité à accéder à l'extérieur de la fonction signifie que, d'une manière générale, il est impossible de comparer des fonctions.

Cependant, sur le plan pratique, les bibliothèques d'analyse syntaxique telles que Esprima ou Acorn peuvent s'avérer très utiles. Celles-ci vous permettent de créer un "arbre de syntaxe abstraite" (AST), qui est une description JSON de votre programme. Par exemple, les fonctions de votre return 1 ressemblent à ceci 

ast = acorn.parse('return 1', {allowReturnOutsideFunction:true});
console.log( JSON.stringify(ast), null, 2)
{
  "body": [
    {
      "argument": {
        "value": 1,              // <- the 1 in 'return 1'
        "raw": "1",
        "type": "Literal"
      },
      "type": "ReturnStatement" // <- the 'return' in 'return 1'
    }
  ],
  "type": "Program"
}
// Elided for clarity - you don't care about source positions

Le AST contient toutes les informations nécessaires pour effectuer des comparaisons - il s’agit de la fonction Javascript sous forme de données. Vous pouvez normaliser les noms de variables, vérifier les fermetures, ignorer les dates, etc. en fonction de vos besoins.

Il existe toute une panoplie d’outils et de bibliothèques pour simplifier le processus, mais même dans ce cas, il faudra probablement beaucoup de travail et ne sera probablement pas pratique, mais c’est surtout possible.

15
Julian de Bhal

Vous pouvez comparer deux variables pouvant contenir des références de fonction pour voir si elles se rapportent exactement à la même fonction, mais vous ne pouvez pas vraiment comparer deux fonctions distinctes pour voir si elles font la même chose.

Par exemple, vous pouvez faire ceci:

function foo() {
    return 1;
}

var a = foo;
var b = foo;

a == b;   // true

Mais vous ne pouvez pas faire ceci de manière fiable:

function foo1() {
    return 1;
}

function foo2() {
    return 1;
}

var a = foo1;
var b = foo2;

a == b;   // false

Vous pouvez voir ce second ici: http://jsfiddle.net/jfriend00/SdKsu/

Il y a des cas où vous pouvez utiliser l'opérateur .toString() sur des fonctions, mais cela consiste à comparer une conversion littérale de votre fonction à une autre qui, même si elle ne fonctionne pas correctement, ne fonctionnera pas. Je ne peux penser à aucune situation où je recommanderais cela comme un mécanisme de comparaison fiable. Si vous envisagiez sérieusement de le faire de cette façon, je demanderais pourquoi? Qu'est-ce que vous essayez vraiment d'accomplir et d'essayer de trouver un moyen plus robuste de résoudre le problème.

8
jfriend00

toString () sur une fonction retourne la déclaration exacte. Vous pouvez modifier le code de jfriend00 pour le tester.

Cela signifie que vous pouvez tester si vos fonctions sont exactement les mêmes, y compris les espaces et les nouvelles lignes que vous y avez insérées.

Mais vous devez d'abord éliminer la différence dans leurs noms.

function foo1() {
    return 1;
}

function foo2() {
    return 1;
}

//Get a string of the function declaration exactly as it was written.
var a = foo1.toString();
var b = foo2.toString();

//Cut out everything before the curly brace.
a = a.substring(a.indexOf("{"));
b = b.substring(b.indexOf("{"));

//a and b are now this string:
//"{
//    return 1;
//}"
alert(a == b); //true.

Comme les autres l'ont dit, cela n'est pas fiable, car un seul espace de différence rend la comparaison fausse.

Mais que se passe-t-il si vous l'utilisez comme mesure de protection? ("Quelqu'un a-t-il modifié ma fonction depuis que je l'ai créée?") Vous pouvez alors souhaiter ce genre de comparaison stricte.

1
Amaroq

Convertissez une fonction en chaîne, puis remplacez le saut de ligne et l'espace avant de comparer:

let a = function () {
  return 1
};

let b = function () {
  return 1
};

a = a.toString().replace(/\n/g, '').replace(/\s{2}/g, ' ');
b = b.toString().replace(/\n/g, '').replace(/\s{2}/g, ' ');

console.log(a); // 'function () { return 1}'
console.log(b); // 'function () { return 1}'
console.log(a === b); // true

b = function () {
  return 2
};

b = b.toString().replace(/\n/g, '').replace(/\s{2}/g, ' ');

console.log(b); // 'function () { return 2}'
console.log(a === b); // false

b = () => 3;

b = b.toString().replace(/\n/g, '').replace(/\s{2}/g, ' ');

console.log(b); // '() => 3'
console.log(a === b); // false

p/s: Si vous utilisez ES6, essayez d'utiliser let au lieu de var.

0
Foo