Ad

Sorting Behavior On Passing Function Vs. Passing Anonymous Closure

- 1 answer

Problem and Question

I have a small application that asynchronously gets JSON data from a server, formats it into a table, and allows the user to perform some simple operations on the table (sorting, filtering, etc.).

One of the sorting functions accesses DOM elements in the previously-generated table (definition given below).

    var SORT = function(){
             var my = {};
             // public methods
             my.byName = function(sign){    
                     (!sign) ? sign=1 : 1;
                     var T = $("#resultArea").find("tbody");
                     var R = T.children("tr");
                     R.sort( function(a,b){
                             var an = $(a).attr("data-name");
                             var bn = $(b).attr("data-name");
                             return sign * an.localeCompare(bn);
                             });
                     R.detach().appendTo(T);
                    }
...
return my; }();

When specifying it as a callback for an element the user can click on, I have two formulations.

$("#sort-button").click(SORT.byName); (pass function as argument)

OR

$("sort-button").click(function(){SORT.byName();}); (pass anonymous closure that calls the function)

The first option fails, but the second works. Here is how it fails on a test case with 536 rows to be sorted:

  • Former row 2 (which precedes row 1 in alphabetical order) is moved to position 268.
  • Former row 269 is moved to position 536.
  • Former row 536 is moved to position 2.

I have tried and failed to construct a MWE that fails in the same way (will update question once I succeed). Questions are: What went wrong? Why does using the anonymous closure work?

Update

An earlier version of the snippet had been sanitized to remove the parameter sign and the line at the start (which would set sign to 1 if it evaluated to false or was undefined), the idea being that by passing sign=-1 as a parameter, a descending sort could be done. When I removed sign from the definition in the live code, the problem went away.

So I found that the offending line is (!sign) ? sign=1 : 1; When replaced by the more transparent if(sign !== undefined){sign=1;} the problem goes away. I think what it does is that it sets globalsign to one on the first pass, then on the second pass, sign is defined so it returns1, and the function ends (only one pass having been completed).

Then why does this error not crash the sort on the anonymous closure approach?

Ad

Answer

As you found out, the problem comes from that sign parameter. When you pass a function as the event handler and call SORT.byName() without arguments, you'll get sign=1 and everything is as expected. But when you pass the function directly as the handler, it will get called with the Event object as its argument. Suddenly you have an object in your sign variable, and multiplying that with a number will yield NaN (an invalid result for a comparison function), which completely messes up your sort.

When replaced by the more transparent if(sign !== undefined){sign=1;} the problem goes away. I think what it does is that it sets global sign to one on the first pass…

Nope. There is no global sign variable at all. And I guess you actually wanted if (sign === undefined) sign = 1;. Which wouldn't work as well when passing in events, so you might want to use if (!Number.isFinite(sign)) sign = 1;.

Ad
source: stackoverflow.com
Ad