Husman
Husman

Reputation: 6909

Javascript prototype constant declaration

I am working with a RESTful API, and my Javascript code is making REST queries via jQuery's $.ajax() call.

I have implemented a javascript Rest class, which I will show below (greatly simplified):

var Rest = function (baseUrlPath, errorMessageHandler) {
        ...
    };

// Declare HTTP response codes as constants
Rest.prototype.STATUS_OK = 200;
Rest.prototype.STATUS_BAD_REQUEST = 400;

... // other rest methods 

Rest.prototype.post = function (params) {
        $.ajax({
            type: 'POST',
            url: params.url,
            data: params.data,
            dataType: 'json',
            contentType: 'application/json; charset=utf-8',
            beforeSend: this._authorize,
            success: params.success,
            error: params.error || this._getAjaxErrorHandler(params.errorMessage)
        });
        };

... // more rest methods

Rest.prototype.executeScenario = function (scenarioRef) {
        var self = this;

        this.post({
            url: 'myurlgoeshere',
            data: 'mydatagoeshere',
            success: function (data, textStatus, xhr) {
                if (xhr.status == 200) {
                    console.log("everything went ok");
                }
            },
            error: function (xhr, textStatus, errorMsg) {
                // TODO: constants
                if (404 == xhr.status) {
                    self.errorMessageHandler("The scenario does not exist or is not currently queued");
                } else if (403 == xhr.status) {
                    self.errorMessageHandler("You are not allowed to execute scenario: " + scenarioRef.displayName);
                } else if(423 == xhr.status) {
                    self.errorMessageHandler("Scenario: " + scenarioRef.displayName +  " is already in the queue");
                }
            }
        });
    };

The code works as intended, however I have decided to add some constants to help beautify the code and improve readability. I have for example several places in my code where I am checking for xhr.status == 200 or xhr.status == 400 and so on.

I can declare class variables as Rest.prototype.STATUS_OK = 200;

But variable is editable, and I cannot think of how to make them constant. In my code for example I can do a this.STATUS_OK = 123; and this will modify the variable. I have played around with the const keyword, with no luck.

i have seen this: Where to declare class constants?, but it was not much help.

Can someone point me in the right direction as to how to make these fields a constant literal instead of a variable?

Upvotes: 8

Views: 10089

Answers (4)

Marcio Junior
Marcio Junior

Reputation: 19128

Javascript doesn't have a good support to create immutable constants. Even the const keyword isn't recommended because doesn't work in some browsers.

I think the best way todo it is using Object.freeze:

Rest.Status = {};
Rest.Status.Ok = "Ok";
Object.freeze(Rest.Status);

Object.freeze will silent ignore changes in the Status object. By example:

Rest.Status.Ok = "foo";
Rest.Status.Ok; //=> "Ok"

But just work in ECMAScript 5 or above.

Above I have placed the status in a Status object, I think that it is more interesting than prototype, because prototype is more close to instance methods, properties etc. And the Status object seen like a enumeration.

Upvotes: 0

apsillers
apsillers

Reputation: 115940

Using ECMAScript 5's Object.defineProperty you can make a value un-settable:

Object.defineProperty(Rest, "STATUS_OK", {
  enumerable: false,   // optional; if you care about your enumerated keys
  configurable: false,
  writable: false,
  value: 200
});

Or, since those are the default values, simply do:

Object.defineProperty(Rest, "STATUS_OK", { value: 200 });

This makes Rest.STATUS_OK yield 200 when accessed, but it will not respond to attempts to redefine it or delete it. Furthermore, configurable: false will prevent any attempt to redefine the property with a subsequent defineProperty call.

However, this doesn't work in older browsers that don't support ES5's defineProperty (notably IE8 and below).

Upvotes: 13

pawel
pawel

Reputation: 36965

You could define the statuses as getters, but AFAIK this won't work in IE8 and older.

var Rest = function (baseUrlPath, errorMessageHandler) {
        this.STATUS_OK = 123; // trying to override.
    };

// Declare HTTP response codes as constants
Rest.prototype = {
    get STATUS_OK(){ return 200; },
    get STATUS_BAD_REQUEST(){ return 400; }
}

var client = new Rest();
console.log( client.STATUS_OK ); // 200!
client.STATUS_OK = 123;
console.log( client.STATUS_OK ); // still 200!

More on getters and setters: http://ejohn.org/blog/javascript-getters-and-setters/

Upvotes: 1

Dennis
Dennis

Reputation: 14465

This will not be possible in Javascript. Best thing you could probably do, is create some closure like stuff:

var StatusCode = (function() {
    var STATUS_OK = 200,
        STATUS_BAD_REQUEST = 400;

    return {
        getOk: function() {
            return STATUS_OK;
        },
        getBadRequest: function() {
            return STATUS_BAD_REQUEST;
        }
    }

});

And use it like StatusCode.getOk() === 200. This would help you to not be able to change those 'constants', but will again be bad for your readability (this is probably opinion based). I would just keep those constants all uppercase to mark them as constant, although they could be changed.

Upvotes: 2

Related Questions