可以用JavaScript对象文字符号创建只读成员吗?

可以用JavaScript对象文字符号创建只读成员吗?,javascript,Javascript,我有以下JavaScript对象文字通知对象 var Parameters= { modal_window:{ backdrop:true, keyboard:true, show:true, remote:false, type:{ normal:function(){ this.footer.button.accept.type='btn btn-p

我有以下JavaScript对象文字通知对象

var Parameters= {
    modal_window:{
        backdrop:true,
        keyboard:true,
        show:true,
        remote:false,
        type:{
            normal:function(){
                this.footer.button.accept.type='btn btn-primary';
                this.header.type='modal-header';
            },
            success:function(){
                this.footer.button.accept.type='btn btn-success';
                this.header.type='modal-header alert alert-success';
            },
            info:function(){
                this.footer.button.accept.type='btn btn-info';
                this.header.type='modal-header alert alert-info';
            },
            error:function(){
                this.footer.button.accept.type='btn btn-danger';
                this.header.type='modal-header alert alert-error';
            },
            warning:function(){
                this.footer.button.accept.type='btn btn-warning';
                this.header.type='modal-header alert';
            }
        }
    },
    header:{
        title:undefined,
        type:this.window.type.normal.header
    },
    footer:{
        button:
        {
            accept:{
                title:'Accept',
                click:undefined,
                type:undefined
            },
            cancel:{
                title:'Cancel',
                click:undefined
            }
        }
    }
};
是否可以使header.type和footer.button.accept.type只读变量,这些变量只能通过window.type.normal、window.type.success等进行更改

澄清: 我想在这里澄清一下。我的Parameters.header.type 应为只读且应具有默认值。当用户 例如,选择Parameters.modal_window.type.normal 必须更改Parameters.header.type


您可以使其功能如下:

header:{
        title:undefined,
        type: function(){
           return Parameters.modal_window.type.normal.header;
        }
    }

如果需要支持IE8或更早版本,可以创建一个访问器方法来检索值,然后使用私有变量存储实际数据。如果您适当地定义了方法,则可以从它们设置私有变量,但外部世界不能设置私有变量。在IE8中,无法定义只读属性,因此必须使用访问器

请参阅Crockford关于私有成员数据的论文:有关如何设置私有数据的详细信息,您的访问器可以作为接口

如果您愿意要求IE9或更高版本,那么可以通过Object.defineProperty将getter与闭包中的私有变量结合使用。如果没有setter,那么就不能从外部设置它,但是Crockford文章中描述的闭包中定义的方法仍然可以设置私有变量的值。您将拥有一个只读属性,该属性也可以由您自己的一些方法设置。

您可以创建一个并将其设置为不可写。构造函数必须用属性替换这些值。如果属性返回的变量是在闭包中捕获的,并且不暴露于任何其他内容,那么它将与只读一样好。如果没有更改,您甚至不需要闭包,只需使用值配置选项

编辑:根据您的需求

var Properties = function(obj) {
    var makePropRecursive = function(prop) {
        var old_prop = obj[prop];
        delete obj[prop];
        var prop_obj = {};
        for (var attr in old_prop) {
            if (old_prop.hasOwnProperty(attr)) {
                Object.defineProperty(prop_obj, attr, {
                    value: old_prop[attr],
                    writable: false,
                    enumerable: true
                });
            }
        }
        Object.defineProperty(obj, prop, {
            value: prop_obj,
            writable: false,
            enumerable: true
        });
    };
    makePropRecursive('header');
    makePropRecursive('footer');
    return obj;
};

var props = new Properties({
    modal_window:{
        backdrop:true,
        keyboard:true,
        show:true,
        remote:false,
        type:{
            normal:function(){
                this.footer.button.accept.type='btn btn-primary';
                this.header.type='modal-header';
            },
            success:function(){
                this.footer.button.accept.type='btn btn-success';
                this.header.type='modal-header alert alert-success';
            },
            info:function(){
                this.footer.button.accept.type='btn btn-info';
                this.header.type='modal-header alert alert-info';
            },
            error:function(){
                this.footer.button.accept.type='btn btn-danger';
                this.header.type='modal-header alert alert-error';
            },
            warning:function(){
                this.footer.button.accept.type='btn btn-warning';
                this.header.type='modal-header alert';
            }
        }
    },
    header:{
        title:"Whatever",
        type:"Type"
    },
    footer:{
        button:
        {
            accept:{
                title:'Accept',
                click:undefined,
                type:undefined
            },
            cancel:{
                title:'Cancel',
                click:undefined
            }
        }
    }
});

console.log(props.header);
props.header = 17;
props.header.type = 18;
props.header.title = 19;
console.log(props.header);
props.header未更改:输出显示

Object {title: "Whatever", type: "Type"}
Object {title: "Whatever", type: "Type"} 
它是3am,递归函数不是,所以你只能修复一个对象的一个级别;此外,如果将值复制到该对象上,而不是返回obj,则会更好;但要把它润色一下应该不难


如果需要更改值,可以在构造函数中设置整个对象的私有副本,然后生成getter get:functionname{return stuff.from.the.original.object}。

在较新版本的JavaScript中,可以定义属性访问的工作方式:

var yourObj = function() {
  var readOnly = "cannot be changed";
  return {
    get readOnly() { return readOnly; },
    set readOnly(v) { return; },

    specialSetter: function(something) {
      if (something == "magic number") {
        readOnly = "oops maybe it can";
      }
    }
  };
}();
现在,代码可以获得如下值:

var theValue = yourObj.readOnly;
无需进行函数调用。但是,如果它试图更改该值:

yourObj.readOnly = "hello world";
那么什么也不会发生

setter或任何其他函数都可以在需要时更新访问readOnly属性时返回的值。但是,任何直接设置属性的尝试都不会起任何作用,除非setter函数决定它喜欢该值

编辑您可能希望使specialSetter成为只读的,尽管没有任何东西能够打断闭包。另外,您可能希望使用Object.defineProperty使readOnly不可写,但我不知道这是否正确。

关于:Object.freeze如何

您可以在此处找到更多信息:

因此:

然后在您希望能够设置它们的函数中,您可以解冻它们、更改它们,然后重新冻结它们

您绝对无法在程序中的任何其他位置错误地更改冻结的对象


这适用于IE9+浏览器、firefox和safari。

不管大家怎么说,您可以在支持Object.defineProperty的现代浏览器中创建只读属性

编辑:

在再次阅读您的问题后,我理解您指的是真正的私有成员或私有变量。这可以通过使用闭包和自定义getter/setter来实现

注意:为了示例,我简化了对象的结构


既然我们知道有可能实施真正的隐私,我不确定这是否真的值得。实施真正的隐私会使设计复杂化,并根据具体情况降低可测试性。一种非常流行的方法是使用命名约定(如_myPrivateVar)简单地标识私有成员。这清楚地表明了itention,并告诉程序员他们应该将该成员视为私有成员。

您可以使用下面的模块模式隐藏变量并防止它们被更改,但这不会阻止任何人更改可访问类型函数

在下面的代码中,header属性被更改为_header并成为一个函数。属性类型已更改为_类型,并通过使用对象表示法将返回包装为函数而不是属性来隐藏。有人可以通过重写将类型函数更改为任何他们想要的,但他们不能 更改_类型的值

var Parameters = function () {
var _modal_window = function modal_window() {
    var backdrop = true,
    keyboard = true,
    show = true,
    remote = false;
    return {
        type: {
            normal: function () {
                this.footer.button.accept.type = 'btn btn-primary';
                this.header.type = 'modal-header';
            },
            success: function () {
                this.footer.button.accept.type = 'btn btn-success';
                this.header.type = 'modal-header alert alert-success';
            },
            info: function () {
                this.footer.button.accept.type = 'btn btn-info';
                this.header.type = 'modal-header alert alert-info';
            },
            error: function () {
                this.footer.button.accept.type = 'btn btn-danger';
                this.header.type = 'modal-header alert alert-error';
            },
            warning: function () {
                this.footer.button.accept.type = 'btn btn-warning';
                this.header.type = 'modal-header alert';
            }
        }
    };
}();
var _header = function header() {
    var _type = 'This causes error';//this.window.type.normal.header;
    return {
        title: undefined, type: function () { return _type; }
    };
}();
var _footer = function footer() {
    return {
        button:
    {
        accept: {
            title: 'Accept',
            click: undefined,
            type: undefined
        },
        cancel: {
            title: 'Cancel',
            click: undefined
        }
    }
    };
}();
return {
    modal_window: _modal_window,
    header: _header,
    footer: _footer
};
}();


不,javascript中不允许只读。Object.defineProperty或my使用闭包函数时可以只读。如果您的代码有意义,我会这样做,但当调用this.window.type.normal.header时,这是全局上下文窗口。还有一件事,这是一个什么样的原型设计模式?@andy我认为在现代JavaScript运行时中,通过getter/setter支持,这实际上是可能的。好吧,它不会使它们成为只读的,但我认为这是op试图实现的精神。@pax162我如何改变它而不是其他函数?这比getter和setter定义的出现要早,是吗?@Pointy-你如何私自存储一些东西,然后只从某些方法访问它,而不使用crockford文章中描述的闭包概念?你可以用闭包来实现,所以这一部分当然是准确的。使用getter和setter,通过让getter获取闭包值,可以使它看起来像有一个属性。setter要么什么也不做,要么做一些验证,或者做任何事情。@Pointy-听起来不错。Closure+getter将为您提供一个只读属性,您可以从Closure中定义的其他方法修改该属性。据我所知,在Object.defineProperty中使用这些功能会迫使您使用IE9或更高版本,因此您也必须同意。为什么要投反对票?这篇帖子和评论中有什么不准确的地方?那就没有什么可以改变这个属性了。这与拥有只能在setter函数控制下设置的私有属性并不完全相同,setter函数可能会应用验证规则或其他任何东西。@Pointy好吧,他没有说他想要私有属性,他说他想要只读属性。这就完成了。最后一句谈到属性是可更改的,但只能通过一些显式API。我想使这个参数只能从外部读取。我不需要私人会员。它应该可以从外部访问,以读取而不是写入。@antindexer Right。您希望控制如何更改属性;例如,检查它是数字还是类似的。我想你像我第一次那样误解了这个问题。@plalx:是的,使用了静态只读属性。最后一段简要地解释了如何为可私人写入的只读属性执行此操作,但我真的需要睡眠:p我一读到它,如果它有任何意义的话,你就得到了我的支持。这非常有效。我对你的答案投了赞成票。我也只是在尝试别人的答案。所以请等待我的决定。@antindexer你忘了吗;问题是任何代码都不能写入不可写的只读属性。OP并不是在寻找那种解决方案,按照我的理解。@Pointy,答案的第二部分允许修改变量。@plalx是的,我明白了。这或多或少也是我的答案。你认为强制实施真正的隐私与使用命名约定(如_myPrivateMember)相比如何?这个示例代码非常接近真正的隐私。我认为命名惯例在美学上并不令人满意;是的,我也有同样的感觉,但是我有一种感觉,那就是大多数图书馆都不会为实现真正的私有成员而烦恼,至少从目前为止我所看到的情况来看是这样的。遗憾的是,我们不得不在编写内存效率高的代码和真正的封装代码之间做出选择。实际上,我只是在强制私有模块成员的隐私,而不是类。
var obj = {};

Object.defineProperty(obj, 'someProp', {
    configurable: false,
    writable: false,
    value: 'initial value'
});

obj.someProp = 'some other value';

console.log(obj.someProp); //initial value
var Parameters = (function () {
    var headerType = 'some value'; //private variable

    return {
        modal_window: {
            type: {
                normal: function () {
                    //custom logic
                    headerType = 'some new value'; //set private variable
                }
            }
        },
        header: {
            get type() { return headerType; } //define a getter only

            //for older browsers, you could just define a normal function
            //which you would have to access like Parameters.header.type()
            //type: function () { return headerType; }
        }
    };

})();

var header = Parameters.header;

console.log(header.type); //some value
header.type = 'some other val';
console.log(header.type); //some value
Parameters.modal_window.type.normal();
console.log(header.type); //some new value
var Parameters = function () {
var _modal_window = function modal_window() {
    var backdrop = true,
    keyboard = true,
    show = true,
    remote = false;
    return {
        type: {
            normal: function () {
                this.footer.button.accept.type = 'btn btn-primary';
                this.header.type = 'modal-header';
            },
            success: function () {
                this.footer.button.accept.type = 'btn btn-success';
                this.header.type = 'modal-header alert alert-success';
            },
            info: function () {
                this.footer.button.accept.type = 'btn btn-info';
                this.header.type = 'modal-header alert alert-info';
            },
            error: function () {
                this.footer.button.accept.type = 'btn btn-danger';
                this.header.type = 'modal-header alert alert-error';
            },
            warning: function () {
                this.footer.button.accept.type = 'btn btn-warning';
                this.header.type = 'modal-header alert';
            }
        }
    };
}();
var _header = function header() {
    var _type = 'This causes error';//this.window.type.normal.header;
    return {
        title: undefined, type: function () { return _type; }
    };
}();
var _footer = function footer() {
    return {
        button:
    {
        accept: {
            title: 'Accept',
            click: undefined,
            type: undefined
        },
        cancel: {
            title: 'Cancel',
            click: undefined
        }
    }
    };
}();
return {
    modal_window: _modal_window,
    header: _header,
    footer: _footer
};
}();