Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to make a function self aware without external input

Background

I want a function keeping track of its own state:

var myObject = {

    myFunction: function () {

        var myself = this.myFunction;
        var firstTime = Boolean(!myself.lastRetry);

        if (firstTime) {

            myself.lastRetry = Date.now();
            return true;
        }

        // some more code

    }
}

The problem with the above code is that the value of this will depend on the site of the function call. I want the function to be able to refer to itself without using:

  • myObject.myFunction
  • .bind()
  • .apply()
  • .call()

Question

Is it possible to give a function this kind of self awareness independent of its call site and without any help from external references to it?

like image 318
rabbitco Avatar asked Feb 08 '23 01:02

rabbitco


1 Answers

If you want to store that state on the function instance, give the function a name, and use that name within it:

var myObject = {

    myFunction: function theFunctionName() {
    //                   ^^^^^^^^^^^^^^^--------------------- name
        var firstTime = Boolean(!theFunctionName.lastRetry);
    //                           ^--------------------------- using it
        if (firstTime) {

            theFunctionName.lastRetry = Date.now();
    //      ^------------------------------------------------ using it
            return true;
        }

        // some more code

    }
};

You'd do that whenever you want to use a function recursively as well. When you give a name to a function that way (putting the name after function and before (), that name is in-scope within the function's own code. (It's not in-scope for the code containing the function if it's a function expression, but it is if it's a function declaration. Yours is an expression.)

That's a named function expression (where previously you had an anonymous function expression). You may hear warnings about NFEs, but the issues various JavaScript implementations had with them are essentially in the past. (IE8 still handles them incorrectly, though: More in this post on my blog.)

You might consider keeping that state somewhere private, though, via an IIFE:

var myObject = (function(){
    var lastRetry = null;
    return {
        myFunction: function() {
            var firstTime = Boolean(!lastRetry);
            if (firstTime) {

                lastRetry = Date.now();
                return true;
            }

            // some more code

        }
    };
})();

Now, nothing outside that outer anonymous function can see lastRetry at all. (And you don't have to worry about IE8, if you're supporting stubborn XP users. :-) )


Side note: The unary ! operator always returns a boolean, so your

var firstTime = Boolean(!theFunctionName.lastRetry);

...is exactly equivalent to:

var firstTime = !theFunctionName.lastRetry;

...but with an extra unnecessary function call. (Not that it hurts anything.)

like image 195
T.J. Crowder Avatar answered Feb 10 '23 07:02

T.J. Crowder