0

我有以下 JavaScript Object Literal Notiation 对象

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。

4

7 回答 7

4

尽管每个人都这么说,但您可以在支持Object.defineProperty.

var obj = {};

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

obj.someProp = 'some other value';

console.log(obj.someProp); //initial value

编辑:

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

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

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

既然我们知道可以强制执行真正的隐私,我不确定它是否真的值得。强制执行真正的隐私会使设计复杂化并降低可测试性(视情况而定)。一种非常流行的方法是简单地使用命名约定来识别私有成员,例如_myPrivateVar. 这清楚地表明了意图并告诉程序员他们应该将该成员视为私有成员。

于 2013-11-02T17:43:29.900 回答
2

您可以使它们起作用,如下所示:

header:{
        title:undefined,
        type: function(){
           return Parameters.modal_window.type.normal.header;
        }
    }
于 2013-11-02T17:31:22.640 回答
2

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

请参阅 Crockford 关于私有成员数据的论文:http: //javascript.crockford.com/private.html以获取有关如何设置访问器可以作为接口的私有数据的详细信息。

如果您愿意使用 IE9 或更高版本,那么您可以Object.defineProperty()在闭包中使用 getter via 和私有变量。如果没有 setter,则无法从外部设置,但在闭包中定义的方法(在 Crockford 的文章中描述)仍然可以设置私有变量的值。您将拥有一个只读属性,该属性也可以由您自己的一些方法设置。

于 2013-11-02T17:32:45.737 回答
2

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

编辑:根据您的要求,

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

现在是凌晨 3 点,递归函数不是,所以你只能“修复”一个对象的一层;此外,如果将值复制到this而不是返回会更好obj;但擦亮它应该不会太难。

如果您需要更改值,您可以在构造函数中设置整个对象的私有副本,然后创建一个 getter ( get: function(name) { return stuff.from.the.original.object })。

于 2013-11-02T17:35:14.910 回答
1

在较新版本的 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”不可写,但我不知道这是否能正常工作。

于 2013-11-02T17:36:24.763 回答
1

怎么样:Object.freeze()

你可以在这里找到更多信息:MDN Object.freeze

所以:

Object.freeze(Parameters.modal_window.header);
...

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

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

这适用于 IE9+ chrome、firefox 和 safari。

于 2013-11-02T17:41:59.237 回答
1

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

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

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
};
}();
于 2013-11-02T18:39:05.500 回答