0

I assumed that functions could passed around like normal values in Javascript. I thought that they evaluated to some address. From the output of the following example, I seem to be right.

var f = function() { console.info('f'); };
var v = function() { console.info('v'); };

var arr = [];

arr.push(f);
arr.push(f);
arr;            // [function(), function()]
arr.uniq();     // [function()]
arr.push(f);
arr.push(f);
arr.push(v);
arr;            // [function(), function(), function()]
arr.without(f); // [function()]
arr.first()();  // v

However, once I applied this observation to "object oriented" code, uniq and without stopped working.

A = Class.create({
 initialize: function() {
 },
 callback: function() {
 },
 add: function() {
  b.add(this.callback.bind(this));
 }
 remove: function() {
  b.remove(this.callback.bind(this));
 }
});

a = new A();

B = Class.create({
 initialize: function() {
  this.funcs = [];
 },
 add: function(func) {
  this.funcs.push(func);
  this.funcs = this.funcs.uniq();
 },
 remove: function(func) {
  this.funcs = this.funcs.without(func);
 }
});

b = new B();

a.add();  // b.funcs = [function()]
a.add();  // b.funcs = [function(), function()]
a.remove; // b.funcs = [function(), function()]

Every time you call b.add, a.funcs grows larger; uniq does not strip the duplicates. And every time you call b.remove, a.funcs does not shrink; without has failed to find the function.

It appears to me that I'm appending functions that resolve to different address every time. Does bind give a new address each time? How do I fix this? I want to ensure that there's never two of the same function in a's this.funcs; It's okay if two functions defined in two different places do the same thing, but not okay if the same function handle were in the array twice.

Rob W
  • 341,306
  • 83
  • 791
  • 678
JoJo
  • 19,587
  • 34
  • 106
  • 162
  • 2
    From the prototypejs docs for `bind`: *"Binds this function to the given context by wrapping it in another function and returning the wrapper."* So I'd say that yes, you're getting new functions. EDIT: [Here's an example](http://jsfiddle.net/zDpkQ/) – user113716 Feb 23 '11 at 03:27

1 Answers1

2

You've got the right idea - that JS functions are first-class citizens, and that bind returns a new function each time it's called. However, what you want to do is impossible. It is computationally impossible to determine if two different functions do the same thing for all inputs.

There's a similar question I saw a little while ago, let me see if I can dig it up. here it is.

Community
  • 1
  • 1
Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • I only want to have uniquely defined functions in the array. I don't mind if two different functions that do the same thing are both in the array. – JoJo Feb 23 '11 at 03:29
  • 1
    @JoJo: that's all well and good, but how do you define "uniquely defined functions?" (cf. my edit) You could use a hash to enforce "uniqueness" instead of an array, if you have some reasonable way to generate strings that could be used as the hash keys. – Matt Ball Feb 23 '11 at 03:33
  • Consider the two functions: `foo = function(){alert(1);}` and `bar = function(){alert(1);}`. These are two functions that do the same thing but are uniquely defined. They would be allowed to coexist in my array. But two or more `foo`s would not be allowed to coexist in my array. – JoJo Feb 23 '11 at 03:39
  • I could just store the functions as their string representation as someFunction.toString(). This would allow me to use `uniq` and `without` to maintain both functionally unique and uniquely defined functions. But I only wanted to enforce the latter. – JoJo Feb 23 '11 at 03:43
  • 1
    @JoJo: so, you're okay with your array containing both `foo` and `bar` as your comment describes? I still don't really understand what you mean by "uniquely defined functions." Do you mean "distinct function instances?" – Matt Ball Feb 23 '11 at 03:43
  • I guess that's what mean. A *uniquely defined function* should strictly equate to itself in the absence of `bind`. – JoJo Feb 23 '11 at 03:46
  • 1
    @JoJo: then in that case, you need to avoid `bind()`, since it returns new functions. – Matt Ball Feb 23 '11 at 03:55
  • If I don't `bind`, then I cannot use `this` in the callback function. I would have to literally refer to the object as `a`. Isn't this considered poor coding style? If it's the only way, then I guess I'm forced to write ugly code. – JoJo Feb 23 '11 at 03:58
  • 1
    @JoJo: no, actually, if you're careful about how you invoke the callback. When invoking the callback, you can use `callback.apply(/* whatever "this" should be here*/)`. [`Function#apply()`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/function/apply) – Matt Ball Feb 23 '11 at 04:09
  • 1
    @JoJo: ...that said, I don't see why you're creating mutually-dependent "classes." It sounds like you're trying to impose class-based inheritance on top of JS' prototype-based inheritance, which is just a bad idea. I recommend reading [this excellent answer](http://stackoverflow.com/questions/2129789/class-inheritance-in-javascript/2129800#2129800) if you haven't already. But really, you're not even using the `prototype` (not the be confused with the JS library you _are_ using) - all that to say, I don't really see what you're trying to accomplish with these mutually-dependent objects. – Matt Ball Feb 23 '11 at 04:13
  • Here's the full story. I'm making a video player (`B`). I have a class that handles the internals of this video player. I have an encompassing application that uses the video player among other things. I also have a class for such an app (`A`). The app would like to register a callback that the video player should call once all videos in the playlist are played. The video player should have a safety net, preventing multiple instances of the callback from being enqueued. – JoJo Feb 23 '11 at 04:46