Javascript原型常量声明

8

我正在使用RESTful API,并且我的Javascript代码通过jQuery的$.ajax()调用进行REST查询。

我已经实现了一个Javascript Rest类,下面是展示的简化版:

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");
                }
            }
        });
    };

代码按预期运行,但我决定添加一些常量来美化代码并提高可读性。例如,在我的代码中,有几个地方我正在检查xhr.status == 200或xhr.status == 400等。
我可以将类变量声明为Rest.prototype.STATUS_OK = 200; 但是变量是可编辑的,我无法想出如何使它们成为常量。例如,在我的代码中,我可以执行this.STATUS_OK = 123;,这将修改变量。我尝试使用const关键字,但没有成功。
我看到了这个: Where to declare class constants?,但是没有什么帮助。
有人能指导我如何使这些字段成为常量文字而不是变量吗?

请注意,“fields”是 属性,而不是 变量。 对于变量,您只需在支持的情况下使用 const 关键字即可。 但是无论如何,您都会将其限制为本地范围 :-) - Bergi
4个回答

13

通过使用ECMAScript 5的Object.defineProperty,您可以使一个值无法被设置:

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

或者,由于这些是默认值,只需执行以下操作:

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

当访问时,这使Rest.STATUS_OK返回200,但它不会响应重新定义或delete的尝试。此外,configurable: false将防止使用后续的defineProperty调用重新定义属性。

然而,在不支持ES5的defineProperty的旧浏览器中(特别是IE8及以下版本),这种方法无法使用。(参考链接)


这是一个不错的解决方法,但似乎需要编写几行代码(我知道这只是一个语句,但为了可读性仍然需要几行),来设置一个单一的常量,这看起来有点笨拙。当你需要声明很多常量时,这肯定是很费力的。也许随着IE8及以下版本的淘汰,未来会有更好的支持。 - Husman
5
@Husman:实际上,你可以省略除了 value 以外的所有行,因为它们的默认值都是 false - Bergi
@Bergi 好的建议,我已经根据你的建议编辑了我的答案。 - apsillers
@Bergi 看起来仍然像一个hack,因为你需要调用全局的Object和一个defineProperty,然后传入你的Object、变量以及你想设置的属性。大多数编程语言都有'var x'和'const x'或类似的东西,例如final(Java)或#define(C / C ++)。 - Husman
我将选择此答案作为被接受的答案,因为作者和 @Bergi 都提供了一些好建议,但是由于该代码是为企业客户编写的且必须支持 IE 浏览器,因此目前对我来说无法使用。感谢大家的帮助。 - Husman
你不能将window.const()定义为一个帮助函数来完成这个肮脏的工作吗?实际上,这并不算太肮脏... - Steven Lu

2
这在Javascript中是不可能的。你可能做的最好的事情就是创建一些类似于闭包的东西:
var StatusCode = (function() {
    var STATUS_OK = 200,
        STATUS_BAD_REQUEST = 400;

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

});

并且像这样使用:StatusCode.getOk() === 200。这将有助于防止更改这些“常量”,但这可能会影响可读性(这可能是基于观点的)。 我只会将这些常量保持全大写来标识它们是常量,尽管它们可以被更改。


2
+1;此外,没有理由阻止某人简单地用“function() { return "something else"; }”覆盖“getOk”。 - apsillers
2
@apsillers 说得好。总的来说,在JavaScript中你无法安全地保护任何东西。典型的例子是,甚至undefined也可以被覆盖。 - Dennis

1
你可以将状态定义为getter,但据我所知,这在IE8及更早版本中不起作用。
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!

关于getter和setter的更多内容:http://ejohn.org/blog/javascript-getters-and-setters/


Object.defineProperty(Rest.prototype, "STATUS_OK", {get:function(){return 123;}}); 翻译完毕,收工 :-) - Bergi

0

Javascript并不支持创建不可变常量。 甚至const关键字也不被推荐,因为在一些浏览器中无法使用。

我认为最好的方法是使用Object.freeze

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

Object.freeze会静默忽略状态对象中的更改。 例如:

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

但只在ECMAScript 5或以上版本中工作。

我已将状态放置在一个状态对象中,我认为它比prototype更有趣,因为原型更接近实例方法、属性等。而状态对象看起来像是一个枚举。


请注意,这仍允许将Rest.Status属性重新分配给完全不同的对象。 - apsillers
是的,我没有意识到。一个替代方案是创建一个RestStatus对象,但这样会存在两个全局变量:( - Marcio Junior

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接