3

I want to extend Array.sort() to accept another parameter. This question has a solution using a closure, so this code works:

var create_comparator = function(c) {
  return function(a,b) { console.log(c); return a-b };
};

arr.sort( create_comparator('test') );

However, in my case I have functions already defined like this:

var myComp1 = function(a,b) { console.log(c); return a-b };

Returning the pre-defined function doesn't work:

var create_comparator = function(sortfn, c) {
  // Uncaught ReferenceError: c is not defined 
  return sortfn;      
};

var arr = [7, 4, 9, 2, 1];
arr.sort( create_comparator(myComp1, 'test') );

I presume that's because c wasn't defined when the original function was created. I tried return function(a,b) { sortfn(a,b); } to create a new closure but that doesn't work either.

Is it possible, using this set-up, for extra parameters to be available to the pre-defined function? Is there another solution for the problem?

Community
  • 1
  • 1
DisgruntledGoat
  • 70,219
  • 68
  • 205
  • 290

4 Answers4

2

Your examples don't really make it clear what you're doing with the other parameter. I'll still take a stab and propose using partials...like:

var myComp1 = function (c, a, b) { console.log(c); return a-b };
arr.sort(myComp1.bind(null, "test"));

Or if you still want to abstract it to a generator function:

var myComp1 = function (c, a, b) { console.log(c); return a-b };

var create_comparator = function(sortfn, c) {
  return sortfn.bind(null, c);      
};

EDIT: Another way! Probably the best for what you're trying to achieve. And what's cool about this is that you can append any number of arguments to your sort functions, which makes the additional parameters optional so you can use them as normal sort comparators as well.

var myComp1 = function (a, b, c) { if (c) { console.log(c); } return a-b };

var create_comparator = function() {
    var sortfn = arguments[0], 
    partial = Array.prototype.slice.call(arguments, 1);

    return function() {
      var args = Array.prototype.slice.call(arguments).concat(partial);
      return sortfn.apply(null, args);
    }
};
Andrew Kirkegaard
  • 1,676
  • 11
  • 13
2

You certainly can't retroactively modify myComp1 to close over a local variable c from a different scope; that's simply not how closures work, or how local variables work. You should simply define myComp1 correctly in the first place.

But if you can't do that, then there is a sort-of solution.

Because myComp1 references a c that is not an in-scope local variable, what it's really referencing is the global variable c; that is, it's referencing the property named 'c' on the global object (window if the host is a Web-browser). So what you can do is, you can create a wrapper function that copies a local variable onto the global object and then invokes myComp1; something like this:

var wrapped_myComp1 = function (a,b,c) {
    window.c = c;
    var ret = myComp1(a,b);
    delete window.c;
    return ret;
};

It's very ugly, and it means that this function is non-reentrant (because each invocation overwrites each other invocation's c), but it should work.

ruakh
  • 175,680
  • 26
  • 273
  • 307
  • The problem could still be in the fact that these functions can not be declared in the global scope, and within another function. I would not change the global scope, it is better to simply write the function changes the same scope as the `myComp1` – Silver_Clash Feb 23 '13 at 18:08
  • @Silver_Clash: I don't understand your first sentence, but as for your second -- I agree. This is very ugly. – ruakh Feb 23 '13 at 18:12
  • Ok, I try show some example. What if myComp1 define in scope of other function? `function some_func(){ var c; /*this c variable not a global!!!*/var myComp1 = function(a,b)....; wrapped_myComp1 = function (c){//your function wich uses window.c}}` In this case your function didn't work, becouse we use `c` variable not in global scope, and we really don't know what scope uses in author's example. – Silver_Clash Feb 23 '13 at 18:14
  • Idea is that we should not explicitly indicate the global scope in the closure, because in fact we need not possible global scope – Silver_Clash Feb 23 '13 at 18:22
0

Problem with your function myComp1 is that your function tries use undefined global variable c. In JavaScript functions have lexical scope, than means that functions uses the scope in which they are defined. And only closure not enough to resolve your problem. Try assign c variable in closure, like id this code:

 var arr = [1,4,3,5,7,1,2],
        c;//it's a global variable, this is not a good approach. Good approach is change myComp1 definition, if you can.
    var myComp1 = function(a,b) { console.log(c); return a-b };

    var comp = function(prop) {
        c = prop; //this assignment push prop variable to global variable c, that uses in your function
        return myComp1;
    }

console.log(arr.sort(comp('additional_param')));
Silver_Clash
  • 397
  • 1
  • 7
0

This will work

var c = 'test'
var myComp1 = function(a,b) { console.log(c); return a-b };

arr.sort( myComp1 );

Here the closure defines c (but you need it defined as above, if you don't have c defined then you will get the error you got).

Hogan
  • 69,564
  • 10
  • 76
  • 117