No, I don’t like JavaScript. Definitely.

Here is just another misleading faces of the JavaScript (or, better, EcmaScript).
Ten years ago or so, I probably had a better consideration of this language, but I really cannot avoid to compare it with C#. The strong yet clear rules of C# rarely lead to side-effects like this one.

Function: gimme your reference!

As in C#, a function can be referred using a delegate, and its “pointer” is uniquely identified in the program. The same thing is in C/C++: you can refer to a certain function by a simple pointer.
In JavaScript isn’t so simple, because the functions are instantiated on every scope creation. This leads to problems when you wish to subscribe and unsubscribe callbacks, events, and similar.
Honestly, I was unaware about this problem, and I bumped against it using the Socket.io library. Simply I wanted to unsubscribe the events (callbacks) from a socket, but the handler functions were nested inside another function. The result is plain simple: the unsubscription failed.

A neat example…

Consider this minimal pattern for subscribe/unsubscribe a single callback:

var E = (function () {
    var a = [];

    return {
        add: function (fn) {
            a.push(fn);
        },
        remove: function (fn) {
            for (var i = 0; i < a.length; i++) {
                if (a[i] === fn) {
                    a.splice(i, 1);
                    break;
                }
            }
        },
        len: function () {
            return a.length;
        }
    }
})();

Now let’s consider this trivial snippet where we’ll implement a basic test for a callback:

function xyz() {

    return {
        add: function () {
            E.add(f);
        },
        remove: function () {
            E.remove(f);
        }
    }
}

var b = new xyz();
b.add();
b.remove();
alert(E.len());     //yields 0...Correct!

The result is zero, as expected.

In order to understand where the problem is, just add a nested function which will leverage the same un/sub pattern:

function xyz() {

    function f(enable) {
        function handler() {
            //
        }

        if (enable) {
            E.add(handler);
        } else {
            E.remove(handler);
        }
    }

    return {
        f: f,
        add: function () {
            E.add(f);
        },
        remove: function () {
            E.remove(f);
        }
    }
}

Once run the proper test, the result is NOT the expected one:

var b = new xyz();
b.f(true);
b.f(false);
alert(E.len());    //yields 1...WRONG!

As said, the reason is that the “handler” function is instantiated (as a new “object”) every time the containing function “f” is called. Thus, the first “handler” reference won’t be the same as the second call.

Conclusions.

You will say: “that’s fine: it’s all about the language!”. Then I’ll answer: “Okay: may I say that I don’t like that?”.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s