Search code examples
javascriptoopencapsulation

Hide variables within an anonymous JavaScript function but access them using `this`


I'll use the following JavaScript code to demonstrate what I am trying to do.

var App = function() {

    var url
    var width
    var height

    function init()
    {
        url = window.location.href
        width = document.body.clientWidth
        height = document.body.clientHeight
    }

    function run()
    {
        init()
        alert(url + ' - ' + width + 'x' + height)
    }

    return {
        run: run
    }
}()

App.run()

( Run this code at: http://jsfiddle.net/zePq9/ )

The above code achieves three things:

  1. Avoids global variables and functions (except the global object App which is intentionally exposed to the global namespace).
  2. Hides the url, width and height variables and the init() function within the anonmyous function.
  3. Exposes only the run() function outside the anonymous function.

However, I do not like this approach much because the init() and run() functions have to work with variables like url, width and height. In a more involved and larger JavaScript code, it is difficult to see the variable url in a function and tell where it is coming from. One has to do a lot of scrolling to answer questions like: Is it a local variable? Is it a global variable? Is it local only to the anonmyous function?

Right now I am solving this problem with the following code.

var App = function() {

    var self = {
        url: '',
        width: 0,
        height: 0
    }

    function init()
    {
        self.url = window.location.href
        self.width = document.body.clientWidth
        self.height = document.body.clientHeight
    }

    function run()
    {
        init()
        alert(self.url + ' - ' + self.width + 'x' + self.height)
    }

    return {
        run: run
    }
}()

App.run()

( Run this code at: http://jsfiddle.net/cXALf/ )

Now, I can clearly see self.url and tell that this variable is coming from the self object, the scope of which I know is limited to the anonmyous function due to my coding convention of using self object to hold together variables shared by all functions within an anonymous function. Note that self.url is still hidden within the anonymous function and is not visible outside it.

Ideally, I would like to use this.url, this.width, etc. instead of self.url, self.width, etc. But I am unable to arrive at a solution that would keep this.url, this.width, etc. invisible from outside the anonmyous function. Is it possible?


Solution

  • You can use bind to permanently set the this value of each function:

    var App = function() {
    
        var self = {
            url: '',
            width: 0,
            height: 0
        };
    
        var init = function()
        {
            this.url = window.location.href;
            this.width = document.body.clientWidth;
            this.height = document.body.clientHeight;
        }.bind(self);
    
        var run = function()
        {
            init();
            alert(self.url + ' - ' + self.width + 'x' + self.height);
        }.bind(self);
    
        return {
            run: run
        }
    }();
    

    Note the older browser don't support bind, but you can use a polyfill to add it to browsers that don't supply it automatically.

    Note that I don't necessarily think this code is a good idea, but it does do exactly what you want. I personally think there's nothing wrong with using self as an explicit container for your variables, and using this instead may make your code harder for others to understand (as they may assume App is meant to have instances).