30

How would I get a positive test for bar and foo's equality?

 foo = function() {
    a = 1;
 }; 

 bar = function() {
    a = 1;
 }; 

 if (foo === bar) alert('baz');
 if (foo == bar) alert('qux');

Both the above conditionals are false.

Updated - As requested the reason I need to test for function equality

I am building a Publish/Subscribe framework and need to pass the callback inorder to unsubscribe to a topic.

Please see the fiddle: http://jsfiddle.net/jamiefearon/hecMS/47/

Jamie Fearon
  • 2,574
  • 13
  • 47
  • 65
  • May be you meant for the functions to return a value?? return a; missing? – frenchie Aug 31 '12 at 13:41
  • 2
    If you explain why it is you want to do this, it might help people provide helpful answers and comments. – Pointy Aug 31 '12 at 13:42
  • So you want to check whether the contents of two functions is exactly the same? Why exactly do you need to do this? – Alex Turpin Aug 31 '12 at 13:42
  • Pointy - I've updated my answer – Jamie Fearon Aug 31 '12 at 13:57
  • Your fiddle should work fine as long as the callback you pass to `unsubscribe` is reference equal to the one you pass in `subscribe`. Isn't this the way most JS event pub/sub libraries work? What's the problem with that approach? – SimplGy Sep 30 '16 at 20:21

7 Answers7

40

You could check whether the content of two functions is exactly the same by comparing the result of calling toString on them

 var foo = function() {
    a = 1;
 }; 

 var bar = function() {
    a = 1;
 }; 

alert(foo.toString() == bar.toString());​

That will, however, fail if there is but one character that is different. Checking to see if two functions do the same thing, on the other hand, is pretty much impossible.

Alex Turpin
  • 46,743
  • 23
  • 113
  • 145
  • You are correct. Perfectly worked... – Basith Aug 31 '12 at 13:46
  • 40
    But this is also perfectly useless. – Alex Turpin Aug 31 '12 at 13:46
  • 10
    absolutely not! It is essential for functions like lodash debounce. – morels Sep 27 '16 at 08:03
  • 1
    Beware that this may return a false positive – if your functions refer to any variables defined in their containing scope, they could have the exact same source code but behave differently. See [this answer](https://stackoverflow.com/a/32061834/1378487) for an example. – Alex Ryan Jun 24 '21 at 23:55
  • const delayedOnSave = useCallback( debounce((val) => onSave(val), 150), [onSave.toString()] ); – nathan hayfield Jul 19 '21 at 19:32
  • super useful with react, useCallback, and lodash debounce – nathan hayfield Jul 19 '21 at 19:35
  • I would rather use `useMemo` instead of `useCallback` to avoid recreating debounced functions over and over again. `const delayedOnSave = useMemo(() => debounce(val => onSave(val), 150), [onSave.toString()] );` – Yorgi Aug 25 '22 at 13:16
24

The problem is that there're different notions of equality on functions.

  • Reference equality. Functions are stored somewhere in memory, and start from a certain address. For two function names (which are essentially addresses inside) reference equality gives you true iff both names point to the same address. It's a matter when you assign one bar = foo. Reference equality is used in JavaScript.
  • Equality on source code as proposed in another answer. It won't work, if you change any character in function body. Also, toSource is currently (as of 2014) only available in Firefox, as it's not W3C standardized.
  • Syntactic equalities on source code. One could use abstract syntax trees to resist against modifications in spaces and comments. One could use alpha-equivalence (i.e. equivalence of functions, where variables are consistently renamed) as defined in lambda calculus, but also there is
  • Extensional equality, when functions are equal if they work the same way. There's a theorem stating that there's just no algorithm to check for extensional equality of functions, so it's certainly not the one you're searching for. Though, in some more advanced languages than JS there are
  • Intensional equality (where you either prove that functions are equal by hand or your program doesn't compile) and
  • Observational equality (this one is too advanced to explain).

So, take care of what you think equality is.

Ahmad Baktash Hayeri
  • 5,802
  • 4
  • 30
  • 43
polkovnikov.ph
  • 6,256
  • 6
  • 44
  • 79
3

You can test to see whether two variables refer to the exact same function object, and you can convert functions to strings and see if they're really exactly the same, but trying to determine whether two functions do the exact same thing would be a little harder, or maybe a lot harder.

Running a pair of functions through a minifier would be interesting, because minifiers do a considerable amount of static analysis.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • 3
    Not a little harder, but actually impossible. – duri Aug 31 '12 at 13:44
  • 3
    @duri well I've learned around here that saying out lout that something is "impossible" is a good way to be humiliated :-) However I agree with you, in general, because complete static analysis of a dynamic language like JavaScript is not possible. – Pointy Aug 31 '12 at 13:45
  • 4
    Well, the question whether 2 functions are equivalent is related to mathematics rather than to programming but you may be interested in http://stackoverflow.com/questions/1132051/is-finding-the-equivalence-of-two-functions-undecidable – duri Aug 31 '12 at 13:49
  • @duri that's interesting, but I think (perhaps incorrectly) that having access to the actual makeup of a function is different than the problem of deciding equivalence of two "opaque" functions. For example, it's certainly possible to say that two JavaScript functions that, when stringified, are character-for-character the same are therefore equivalent, to the extent that the behavior of the runtime can be assumed to be consistent. – Pointy Aug 31 '12 at 13:57
  • 5
    @Pointy I believe this to be incorrect. If you have two functions which are identical character-for-character then they still may operate differently because they may reference variables in their static closure. e.g. function() { return x } will return differently depending on what x is in the scope where the function is defined. – Hugheth May 12 '15 at 16:18
  • @Hugheth that's a good point. Depending on the context, one might still consider the functions to be "equivalent", but on the whole I think such a pursuit is fundamentally misguided :) – Pointy May 12 '15 at 16:25
3

If you look at the Node.js source for events

https://github.com/nodejs/node/blob/master/lib/events.js

you can see that the core team uses === to compare functions for equality.

I am not sure how === is implemented for functions, but I sure hope it's not toString(), since two different functions might have the same toString() result.

halfer
  • 19,824
  • 17
  • 99
  • 186
Alexander Mills
  • 90,741
  • 139
  • 482
  • 817
  • 3
    This uses a reference equality check, same as javascript's standard behavior: https://github.com/nodejs/node/blob/master/lib/events.js#L329 – SimplGy Sep 30 '16 at 20:20
1

Basically, it's impossible in general. There is no way to test functional equality in javascript. The closest you can get is either compare their code as string for equality.

m4573r
  • 992
  • 7
  • 17
1

It seems like if you need this functionality, it might be better to restructure your program a bit. Something like this could work:

function baz(myInt) { return 3 + myInt; }
function asd(a) { return 3 + a; }

var foo = baz;
foo(1); //returns 4

var bar = baz;
bar(3); //returns 6

foo === bar; //true

bar = asd;
bar(3); //returns 6

foo === bar; //false
BLSully
  • 5,929
  • 1
  • 27
  • 43
0

Simple approach: If you implement your subscribe API to return a unique ID every time it is invoked, then it will be as simple as invoking unsubscribe API with that ID. Internally in simple scenario you would use a simple array to store all subscribers but with this approach you will have to store. An ID can be a simple counter that you can increment with every single subscribe call.

{ID: subscriber' callback function}

So iterate over all subscribers when you want to publish to all subscribers.

Advanced approach: Instead of accepting a simple function name in subscribe API, accept an object of a class which should have an onMessage function (or whatever name you want to give this method). Then you can simply pass same instance of object to unsubscribe API to get yourself unregistered. When you need to notify subscriber you can invoke instance.onMessage function of all subscribers. Comparing two object reference is easy.

TechMaze
  • 477
  • 2
  • 9