70

How do I compare 2 functions in javascript? I am not talking about internal reference. Say

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

Is it possible to compare a and b ?

ObiHill
  • 11,448
  • 20
  • 86
  • 135
onemach
  • 4,265
  • 6
  • 34
  • 52
  • That JavaScript won't even parse do to a syntax error ... but, what do you mean by "compare"? Various browsers support "toString" on function-objects differently, so that may work in your case. (But not in this one because it won't parse so there is nothing assigned to `a` or `b`). I also think there are some libraries to "reflect" on JavaScript [in script blocks]. –  Mar 22 '12 at 06:49
  • as everything is an object in JS - you probably could try to find smth like object comparison (http://stackoverflow.com/questions/1068834/object-comparison-in-javascript) – shershen Mar 22 '12 at 06:58

6 Answers6

69
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( )  
onemach
  • 4,265
  • 6
  • 34
  • 52
Rony SP
  • 2,618
  • 15
  • 16
  • 3
    note that this works with anonymous functions (which, fair enough, is what the OP asked). If you have var a = function a(){.... and var b = function b(){... (which many people do, because then you get more meaningful messages in your stack) then there is no way – gotofritz Jun 07 '13 at 15:21
  • 1
    Be aware that this will also evaluate as `true` for two different functions with the same code (may happen in real word with two class methods). – bitsmanent Dec 13 '19 at 15:55
  • 2
    Please note that this method will fail for functions with equal syntax tree but with different spacing. – Wong Jia Hau May 17 '20 at 02:55
  • 1
    Beware that if your functions refer to any variables defined in their containing scope, a `toString` equality check may return true even if the functions are not in fact equal. See Julian's [answer](https://stackoverflow.com/a/32061834/1378487) for an example. – Alex Ryan Jun 24 '21 at 23:50
31

Closures mean that you need to be very careful what you mean when you say "compare". For example:

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, the same code
// In another sense they're different:
a() === b(); // false, different answers
// In a third sense even that's not enough:
a2 = closure('a');
a() === a2(); // true
a === a2; // false, not the same object

The ability to reach outside the function means that in a general sense, comparing functions is impossible.

However, in a practical sense you can get a very long way with Javascript parsing libraries like Esprima or Acorn. These let you build up an "Abstract Syntax Tree" (AST), which is a JSON description of your program. For example, the ast your return 1 functions looks like this

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

The AST has all the information you need to make comparisons - it is the Javascript function in data form. You could normalize variable names, check for closures, ignore dates and so on depending on your needs.

There are a bunch of tools and libraries to help simplify the process but even so, it's likely to be a lot of work and probably not practical, but it is mostly possible.

Julian de Bhal
  • 901
  • 9
  • 12
  • 1
    This is a good question. I made the answer worse trying to explain there, so I'll explain here instead - in the add/remove event listeners case, javascript is doing a reference check. It's not comparing functions at all, it's asking "is this the same object?" (and functions are just another sort of object). References in javascript can be quite subtle and confusing, unfortunately. This seems is the clearest I found: https://stackoverflow.com/questions/16255378/javascript-object-identities otherwise google "javascript references vs values", but plan do do some experimenting in the console – Julian de Bhal Oct 28 '22 at 00:45
8

You can compare two variables that might contain function references to see if they refer to the exact same function, but you cannot really compare two separate functions to see if they do the same thing.

For example, you can do this:

function foo() {
    return 1;
}

var a = foo;
var b = foo;

a == b;   // true

But, you can't reliably do this:

function foo1() {
    return 1;
}

function foo2() {
    return 1;
}

var a = foo1;
var b = foo2;

a == b;   // false

You can see this second one here: http://jsfiddle.net/jfriend00/SdKsu/

There are some circumstances where you can use the .toString() operator on functions, but that's comparing a literal string conversion of your function to one another which, if even off by a teeny bit that is inconsequential to what it actually produces, will not work. I can think of no situation where I would recommend this as a reliable comparison mechanism. If you were seriously thinking about doing it this way, I'd ask why? What are you really trying to accomplish and try to find a more robust way of solving the problem.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
6

toString() on a function returns the exact declaration. You can modify jfriend00's code to test it out.

This means you can test to see if your functions are exactly the same, including what spaces and newlines you put in it.

But first you have to eliminate the difference in their names.

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.

As the others said, this is unreliable because a single whitespace of difference makes the comparison false.

But what if you're employing it as a protective measure? ("Has someone altered my function since I created it?") You may actually desire this kind of strict comparison then.

Amaroq
  • 91
  • 1
  • 5
0

ES6+ clean solution using template literals:

const fn1 = () => {}
const fn2 = () => {}

console.log(`${fn1}` === `${fn2}`) // true

Basically means:

console.log(fn1.toString() === fn2.toString()) // true
Wenfang Du
  • 8,804
  • 9
  • 59
  • 90
-1

Convert function to string, then, replace line-break and space before comparing:

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: If you are using ES6, try to use let instead of var.

Tân
  • 1
  • 15
  • 56
  • 102
  • is not it work if declare the function like " function a(){return 1;} " ? – Tarek Salah uddin Mahmud Aug 25 '16 at 12:08
  • @TarekSalahuddinMahmud You can try again. I just check again. It's working. – Tân Aug 25 '16 at 12:11
  • `let`is not always faster than `var`. They simply scope the variable differently, but they are usually equally efficient. `const`, however, is definitely faster because it allows JIST optimizations. – Jack G Sep 04 '18 at 10:50