web-dev-qa-db-de.com

wie vergleiche ich 2 Funktionen in Javascript

Wie vergleiche ich zwei Funktionen in Javascript? Ich spreche nicht von internen Referenzen. Sagen 

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

Kann man a und b vergleichen?

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

Schließungen bedeuten, dass Sie sehr vorsichtig sein müssen, was Sie meinen, wenn Sie "Vergleichen" sagen. Zum Beispiel:

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

Die Fähigkeit, außerhalb der Funktion zu gelangen, bedeutet, dass ein Vergleich von Funktionen im Allgemeinen unmöglich ist.

Mit Javascript-Parsing-Bibliotheken wie Esprima oder Acorn können Sie jedoch praktisch weit kommen. Damit können Sie einen "Abstract Syntax Tree" (AST) aufbauen, bei dem es sich um eine JSON-Beschreibung Ihres Programms handelt. Zum Beispiel sehen die ast Ihre return 1-Funktionen so aus 

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

Das AST enthält alle Informationen, die Sie zum Vergleichen benötigen - es handelt sich um die Javascript-Funktion in Datenform. Sie können Variablennamen normalisieren, nach Schließungen suchen, Datumsangaben usw. ignorieren, je nach Ihren Anforderungen.

Es gibt eine Reihe von Tools und Bibliotheken, die zur Vereinfachung des Prozesses beitragen. Trotzdem ist dies wahrscheinlich eine Menge Arbeit und wahrscheinlich nicht praktikabel, aber es ist meistens möglich.

15
Julian de Bhal

Sie können zwei Variablen vergleichen, die Funktionsreferenzen enthalten können, um zu sehen, ob sie sich auf genau dieselbe Funktion beziehen. Sie können jedoch nicht wirklich zwei separate Funktionen vergleichen, um zu sehen, ob sie dasselbe tun.

Zum Beispiel können Sie dies tun:

function foo() {
    return 1;
}

var a = foo;
var b = foo;

a == b;   // true

Sie können dies jedoch nicht zuverlässig tun:

function foo1() {
    return 1;
}

function foo2() {
    return 1;
}

var a = foo1;
var b = foo2;

a == b;   // false

Sie können diese zweite hier sehen: http://jsfiddle.net/jfriend00/SdKsu/

Es gibt einige Situationen, in denen Sie den .toString()-Operator für Funktionen verwenden können, aber das vergleicht eine Konvertierung Ihrer Funktion durch einen wörtlichen String miteinander, die, wenn auch nur um ein Teeny-Bit deaktiviert, das für das, was sie tatsächlich erzeugt, unwichtig ist, nicht funktionieren wird. Ich kann mir keine Situation vorstellen, in der ich das als zuverlässigen Vergleichsmechanismus empfehlen würde. Wenn du ernsthaft darüber nachdenken würdest, würde ich fragen warum? Was versuchen Sie wirklich zu erreichen und versuchen, einen robusteren Weg zur Lösung des Problems zu finden.

8
jfriend00

toString () für eine Funktion gibt die genaue Deklaration zurück. Sie können den Code von jfriend00 ändern, um ihn auszuprobieren.

Das heißt, Sie können testen, ob Ihre Funktionen genau gleich sind, einschließlich der Leerzeichen und Zeilenumbrüche, die Sie darin einfügen.

Aber zuerst müssen Sie den Unterschied in ihren Namen beseitigen.

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.

Wie die anderen bereits sagten, ist dies unzuverlässig, da ein einzelner Whitespace des Unterschieds den Vergleich falsch macht.

Aber was ist, wenn Sie es als Schutzmaßnahme einsetzen? ("Hat jemand meine Funktion geändert, seit ich sie erstellt habe?") Vielleicht möchten Sie dann einen solchen strengen Vergleich.

1
Amaroq

Funktion in String umwandeln, Zeilenumbruch und Leerzeichen vor dem Vergleich ersetzen:

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: Wenn Sie ES6 verwenden, versuchen Sie, let anstelle von var zu verwenden.

0
Foo