Wednesday, January 28, 2015

JavaScript "constants"

I did a bit of research about constants in JavaScript. Well guess what, there are no constants in JavaScript. :)

So how do you implement "constants" with JavaScript patterns and syntax?

Note: The next text might require more readings but if will help you learn lot of JavaScript basics. Probably you will never have to use this code, since existing JS librarires have already solved this issue somehow.

Constants are important concept of every programming language and definetelly you should use them whenever possible. For example let just say you have an error message that needs to be printed in the output field from several places in your code. How are you sure that each time you write that string of message is written without missing letters? Sometimes you are tired, sometimes you rush and therefore errors are inevitable. So to prevent this you create a constant that contain that string and use the constant instead of writing the string.

Example constant: CANNOT_DIVIDE_BY_ZERO = "Cannot divide by zero!";

I have been developing a practice application which you can access live here: http://jspractices-blueplayer.rhcloud.com/Calculator/ and the docs are here: http://jspractices-blueplayer.rhcloud.com/Calculator/docs
Also you may preview the HTML/JavaScript code here: https://github.com/bluePlayer/practices/tree/master/CalculatorJS.
The code also passes JSLint test, with only "jslint browser: true" option.

There are two important functions which you should check, Constants() and namespace().
Constants() is a constructor function and namespace() creates subobject of the main application object called window.APP.
Lets start with namespace() function. It recieves two parameters, first the name of the subobject that will be created and second is the configuration object which contains necessary properties and methods that will be contained in this new subobject. namespace() creates new subobject Constants using the new keyword, secondly creates constMap subobject which will contain accessible keys for each of the constants.

Example:

window.APP.namespace('Trigonometry', (function () {
    return {
        PI: 3.14,
        doSomeStuff: function () {

        }
    }
}()));

This example creates Trigonometry subobject of window.APP and Trigonometry contains two subobjects, Constants with methods, get(), set(), isDefined() and list() and constMap which has one key/value, PI: "PI" and ofcourse the function doSomeStuff() which does nothing.

So with my solution you can create immutable constants by just passing key/value pair (key must be all capitalized letters) and the code will create it for you. Well if we create a PI constant how do we get the value? The solution is straight forward but it looks very ugly and big.

Example:

console.log(window.APP.Trigonometry.Constants.get(window.APP.Trigonometry.constMap.PI));
// prints 3.14

Alternatively you can pass the name of the constant if you know it:

console.log(window.APP.Trigonometry.Constants.get("PI"));

hahah :D

To improve this solution a bit I created an application wide function in APP namespace called getConst(). You send the path of the constant in string format.

Example:  

console.log(window.APP.getConst('APP.Trigonometry.PI')); // prints 3.14

What happens in this function is parsing the string parameter and getting to the constant value trough the application tree. But this solution is against the "Constants" concept. Either I should be careful to write that string parameter properly or I should create a variable/property inside APP to contain that string and then use it in the above example, like so:

window.APP = window.APP || (function () {
    var TRIGONOMETRY_PI =  'APP.Trigonometry.PI';
    return { 
        getConst: function (constantPath) {
            // code
        },
        doSomeStuff: function () {
               console.log(this.getConst(TRIGONOMETRY_PI)); 
        }
    }
}()));

window.APP.doSomeStuff(); // prints 3.14


So this brings us to the best solution to the problem, and that is creating variables with capitalized letters in closure and then using capitalized accessor methods for them.

Example: I will create GRAVITY = 9.8 constant in new subobject of APP, called Physics.
window.APP.namespace('Physics', (function () {
    var GRAVITY = 9.8;
    return {
          GRAVITY: function () {
               return GRAVITY;
         }
    };
}()));

console.log(window.APP.Physics.GRAVITY()); // prints 9.8

This will create subobject Physics in window.APP and it will contain constMap object with one key/value pair, GRAVITY which is function(). I changed namespace() function to create function in constMap if the capitalized key contains value of a function instead of a value.  If I just send this key/value pair: GRAVITY: 9.8, the namespace() function would have created key/value pair in constMap, like so: GRAVITY: "GRAVITY" and you have to access constant's value using the long statement in the previous examples.

To clear things up, you don't even need to create Constants() constructor in your application, you can use variable with closure and accessor methods with capitalized names to represent constants. I created this constMap subobject just to separate constants from other properties/methods in a given object. You don't even need it.

Note: there are some rummors that ECMAScript 7 or was it 6, will contain const keyword. Until then you can use capitalized variable and methods with closure to represent "constants".

If you have any thoughts on the topic, let me know.


No comments:

Post a Comment