Constants

Although there actually are constants in some environments, e.g. in Firefox you can use const instead of var, in general in JavaScript there are no constants.

Workaround

To work around that limitation, people often use ALLCAPPS to denote "hey, don't touch this var, it's meant to be a constant". It's constant-by-convention but the values can be changed by careless programmer on a bad day. If you really want to protect the value, you need to make it private.

PHP-inspired API

In PHP there are the functions define(name, value) to gefine a constant, defined(name) to check if a constant is defined and constant(name) to get the value of a constants when it's name is assembled at runtime and you don't know it a priori. So I thought - well, we can do the same in JavaScript. Only, let's not use global functions, but a constant global var and make the functions method of that global. constant.constant(name) is a little mouthful, so let's make that one constant.get(name)

Implementation

Here's the simple implementation:

"use strict";

var constant = (function () {
    var constants = {},
        ownProp = Object.prototype.hasOwnProperty,
        allowed = {
            string: 1,
            number: 1,
            boolean: 1
        };
    return {
        define: function (name, value) {
            if (this.defined(name)) {
                return false;
            }
            if (!ownProp.call(allowed, typeof value)) {
                return false;
            }
            constants[name] = value;
            return true;
        },
        defined: function (name) {
            return ownProp.call(constants, name);
        },
        get: function (name) {
            if (this.defined(name)) {
                return constants[name];
            }
            return null;
        }
    };
}());

This is it. Basically protect an object in a closure and don't provide means to change it, but only to add properties to it.

UPDATE: thanks to the comments (see below), there's extra care to check for own properties of the constants private object. This allows to define constants with weird names such as toString and hasOwnProperty. Also only primitive values are allowed to be constants.

Usage

// check if defined
constant.defined("bazinga"); // false

// define
constant.define("bazinga", "Bazinga!"); // true

// check again
constant.defined("bazinga"); // true

// attempt to redefine
constant.define("bazinga", "Bazinga2"); // false

// was it constant or it changed?
// get da, get da, get da value
constant.get("bazinga"); // "Bazinga!"

Tags: , , ,

19 Responses to “Constants”

  1. Derek Gathright Says:

    I’ve always thought all caps is a bad way to denote a constant. a) It conflicts with the other (bad) use of all-caps, globals. b) It doesn’t actually make it immutable. c) It is ugly.

    Nice pattern though. I’ll definitely make use of this in the future.

    +1 for “Bazinga” reference.

  2. Asen Bozhilov Says:

    Interesting pattern. PHP get the idea for `define’ by languages with preprocessor system as “C”, where have directive “#define”.
    Regarding your implementation. There some problems. The main problem is `in` operator. `in` examine prototype chain of object in right side for property name in left hand side expression.
    In your case prototype chain of `constants` looks:

    constants -> Object.prototype -> null

    Object.prototype allow me, to pollute your constants and prevent me to define constants with name as built-in properties of Object.prototype. For example:

    constant.define(‘hasOwnProperty’, 10); //false

    Object.prototype.x = true;
    constant.define(‘x’, false); //false

    You don’t need to examine prototype chain, you should need check only object which refer `constants`.

    var constants = {},
         hasOwnProp = Object.prototype.hasOwnProperty; 
    
    if (!hasOwnProp.call(constants, 'hasOwnProperty')) {
       constants['hasOwnProperty'] = value;
    }
    
  3. Alberto Wagner Says:

    It should be:
    if (!(name in constants)) {

    as “!” operator have higher precedence than “in” operator

    Tested in IE 6 and Firefox 3.6.2

  4. Alberto Wagner Says:

    I really like your approach. It’s a little verbose but it helps to keep some sanity between js files (and people).

  5. jdbartlett Says:

    Nice pattern. Users should beware that “private” objects implemented with closures can still be altered because a pointer is returned. For example:

    constant.define('today', new Date());
    var myDate = constant.get('today');
    myDate.setYear(2009);
    constant.get('today'); // today is now in 2009!
    

    We could include a shallow cloning system inside the “get” method (most libraries already have these), or just be careful to only define numbers and strings as constants.

  6. stoyan Says:

    Thanks for the comments, guys, invaluable!

    @jdbartlett – good point, updated so now define() checks if the value is primitive and refuse to define the constant if not

    @Alberto Wagner – yes, thanks, I now removed in completely

    @Asen Bozhilov – updated based on your comments, check it out. Thanks for the reminder that often there’s more to JS than it looks on the surface :)

  7. Kyle Simpson Says:

    This is a nice implementation and pattern. I’d only make this one comment… when you have one public API function (like “define”) that uses another public API function directly (via “this.defined”), you are slightly susceptible to the global “constants” object having one or more of its public API functions redefined/wrapped, either intentionally or maliciously.

    For instance, if I had code that was trying to “fool” the constants object into allowing me to overwrite a pre-defined constant, because I was trying to maliciously muck with the current pages code, I could redefine the “defined” function, making it always return false, and then call “define()” and it would pass through and redefine the constant errantly, which the page probably wouldn’t want.

    For this reason, I’m a fan of the slightly more verbose option, which is to have public API functions only reference internal, closure-protected functions, not other public functions. That way, a malicious program might try to muck with the public API, but at least dependent calls from one public function to another in the public API couldn’t be messed with. It significantly reduces the vulnerability of the API to redefinition.

    So:

    var constant = (function () {
        var constants = {},
            ownProp = Object.prototype.hasOwnProperty,
            allowed = {
                string: 1,
                number: 1,
                boolean: 1
            };
        function define(name, value) {
             if (defined(name)) {
                 return false;
             }
             if (!ownProp.call(allowed, typeof value)) {
                 return false;
             }
             constants[name] = value;
             return true;
        }
        function defined(name) {
             return ownProp.call(constants, name);
        }
        function get(name) {
             if (defined(name)) {
                 return constants[name];
             }
             return null;
        }
    
        return {
            define:define,
            defined:defined,
            get:get
        };
    }());
    
  8. Asen Bozhilov Says:

    I miss the concept of `allowed`. Purposes by jdbartlett are not related with constant pattern. He talking about `freeze` instead of constants. You can try follow code in SpiderMonkey as you maintained with `const` statement.

    const foo = {bar : 10};
    window.alert(foo.bar); //10
    foo.bar = 20;
    window.alert(foo.bar); //20
    
    foo = false;
    window.alert(foo === false); //false
    

    The value of `foo` is unchangeable, which mean `foo` value is constant value. In ECMAScript terminology that mean `foo` has ReadOnly attribute. Your previous implementation was good, without restriction of value type.

    In ECMA-262-5 there is `Object.defineProperty` with that method can manipulate internal attributes of property and it’s easy to create constants value. However the things for which talking @jdbartlett, ES5 Object constructor has method `freeze`, which do exactly what he want.

    I prefer to use names with upper case letters. That allow me to keep constants for my objects as properties of that object. I always do documentation in which i write about constants in my code. With that convention will be easy for me to migrate to real constant value in ES5 environment.

    Regards for good articles!

  9. stoyan Says:

    Thanks @Kyle, this is a good point. One can never be too paranoid right? :)

    Christian Heilmann … hm, well… christened the pattern you described as “revelation” pattern, or revealing module pattern actually:
    http://www.wait-till-i.com/2007/08/22/again-with-the-module-pattern-reveal-something-to-the-world/

    @Asen – yes, I think the original point by jdbartlett was more along the lines of freezing. But I thought instead of solving the freezing problem, why not just disallow objects to begin with. In PHP for example you can only use scalar values in constants.

    All in all I agree that a simple naming convention should be able to do the trick in most cases. Only that as @Derek says (and Crockford too) the ALL CAPS convention may be reserved for GLOBALS.

  10. Alberto Wagner Says:

    @Kyle: Why would that be useful if I’m easily allowed to do this:

    constant.get_old = constant.get;
    constant.get = function(name) {
    if (name == “bazinga”) {
    return “Not Bazinga!”;
    }

    return this.get_old(name);
    }

    Every call from other APIs will now get the wrong constant.

  11. Kyle Simpson Says:

    @Alberto — sure, you can wrap the public API’s and mess with them. But the key is, the private versions of the functions, AND the private store of data (that is, the actual constants) cannot be messed with. Is it perfectly safe? No. But it’s more safe (IMHO) than the posted version. And the safer the better.

    For instance, consider this… if I have another part of my JS app that I want to use this constants tool with… I can immediately capture references to the 3 public API methods in my own internal closure… once I have those, even if someone later messes with the global public API function references, I still have reference to the original copies that they cannot affect.

    And the reason why my suggestion is better in that case is that with the original posted version, even though I have reference to all 3 API functions in my internal closure, if a malicious piece of code overwrites one of the public API functions, because of lazy resolution of “this.” references, use of the tool is susceptible. Not so with my modification.

  12. Wilco Fiers Says:

    That’s a very cool solution. I’d like to point out though that ‘define’ as a name on the global is also used by the async/require spec in commonJS, so this solution might conflict with implementations such as those used by requirejs.org. Perhaps something like const.define?

  13. gerits aurelien Says:

    I like this approach,
    I use singletons to build scripts,
    but how to define constants with jQuery
    Exemple singleton:

    var ns_jsettingimage = {
    _loadingConfig:function(){
    //my config
    },
    _updateSizeImg:function(formsId){
    $(‘#’+formsId).submit(function(){
    $.notice({
    ntype: “ajaxsubmit”,
    delay: 2800,
    dom: this,
    uri: ‘/admin/imagesize.php’,
    typesend: ‘post’,
    time:2,
    reloadhtml:false
    });
    return false;
    });
    },
    _configUpdateId:function(){
    $(“.forms-config”).each(function(){
    var formsId = $(this).attr(‘id’);
    ns_jsettingimage._updateSizeImg(formsId);
    });
    this._loadingConfig();
    }
    };

    How to define constants in such a singleton

  14. ropa interior Calvin Klein Says:

    I’ve learned some new factors through your blog. One other thing Id like to say may be the truth newer computer system operating systems are most likely to enable a lot extra memory to be utilized, but they also demand extra storage basically to carry out. If peoples computer system cant manage a lot more memory as well as the newest software program program demands that memory space enhance, it’s typically the time to buy a new Pc program. Thanks.

  15. kup cialis Says:

    Spot on with this write-up, I really assume this website needs way more consideration. I’ll probably be again to read much more, thanks for that info.

  16. buy nandrolone Says:

    Hope to receive some assistance from http://www.jspatterns.com if I will have any questions.

  17. Kristy Alyea Says:

    I really like your site and will visit often. Thank you

  18. Casque Beats by Dr.Dre In-Ear Powerbeats prix Says:

    i became woundering how do i be a musician when it is in any iso, psx as well as hombrew game

  19. Keneth Estle Says:

    Technicall-y the only “color of light” does not exist would be black light, as black is perceived to be the absence of all light. :) best diablo 3 gold

Leave a Reply