【问题标题】:Defining read-only properties in JavaScript在 JavaScript 中定义只读属性
【发布时间】:2011-12-07 03:00:27
【问题描述】:

给定一个对象obj,我想定义一个只读属性'prop',并将其值设置为val。这是正确的做法吗?

Object.defineProperty( obj, 'prop', {
    get: function () {
        return val;
    }
});

结果应该是(对于val = 'test'):

obj.prop; // 'test'
obj.prop = 'changed';
obj.prop; // still 'test' since it's read-only

此方法有效:http://jsfiddle.net/GHMjN/
我只是不确定这是否是最简单/最顺畅/最合适的方法......

【问题讨论】:

标签: javascript


【解决方案1】:

您可以改用属性描述符的writable 属性,这样就不需要get 访问器:

var obj = {};
Object.defineProperty(obj, "prop", {
    value: "test",
    writable: false
});

如 cmets 中所述,writable 选项默认为 false,因此在这种情况下您可以省略它:

Object.defineProperty(obj, "prop", {
    value: "test"
});

这是 ECMAScript 5,因此无法在旧版浏览器中运行。

【讨论】:

  • 我不确定我的代码和您的代码是否“在外部”产生完全相同的结果,但您的方法无疑是更合适的方法。
  • 根据 MDN 文档,Object.defineProperty(obj, "prop", {value:"test"}); 等价于上面的代码,因为 writable 默认设置为 false。
  • 请注意,最终对象/数组的 subProperties仍然可以修改。在这种情况下,只有“prop”键不能重新分配。
【解决方案2】:

在新浏览器或 node.js 中,可以使用 Proxy 创建只读对象。

var obj = {
    prop: 'test'
}

obj = new Proxy(obj ,{
    setProperty: function(target, key, value){
        if(target.hasOwnProperty(key))
            return target[key];
        return target[key] = value;
    },
    get: function(target, key){
        return target[key];
    },
    set: function(target, key, value){
        return this.setProperty(target, key, value);
    },
    defineProperty: function (target, key, desc) {
        return this.setProperty(target, key, desc.value);
    },
    deleteProperty: function(target, key) {
        return false;
    }
});

您仍然可以为该对象分配新属性,它们也是只读的。

示例

obj.prop
// > 'test'

obj.prop = 'changed';
obj.prop
// > 'test'

// New value
obj.myValue = 'foo';
obj.myValue = 'bar';

obj.myValue
// > 'foo'

【讨论】:

    【解决方案3】:

    由于旧浏览器(向后兼容),我不得不想出属性的访问器函数。我使它成为bob.js 的一部分:

    var obj = { };
    //declare read-only property.
    bob.prop.namedProp(obj, 'name', 'Bob', true);
    //declare read-write property.
    bob.prop.namedProp(obj, 'age', 1);
    
    //get values of properties.
    console.log(bob.string.formatString('{0} is {1} years old.', obj.get_name(), obj.get_age()));
    //set value of read-write property.
    obj.set_age(2);
    console.log(bob.string.formatString('Now {0} is {1} years old.', obj.get_name(), obj.get_age()));
    
    //cannot set read-only property of obj. Next line would throw an error.
    // obj.set_name('Rob');
    
    //Output:
    //========
    // Bob is 1 years old.
    // Now Bob is 2 years old.
    

    希望对你有帮助。

    【讨论】:

    • 等等,.namedProp(obj, 'foo'),在对象本身上创建.get_foo(),/.set_foo()?这不是很有效。我想我会带一个包装器,例如X(obj).set('foo')/X(obj).get('foo').
    • 澄清:我认为我们在谈论两个不同的事情。您可能希望保持真实对象不变,同时对其进行包装;但我建议更改真实对象并具有表示属性的函数。因为为了向后兼容,旧式 JavaScript 属性可能被视为字段。因此,您需要为其提供访问器(函数)(另外我建议从真实对象中删除传统的 JS 属性,从而完全控制这些属性)。至于包装器,这也不错,但与我的方法不同。
    【解决方案4】:

    我试过了,效果很好……

    element.readOnly = "readOnly"  (then .readonly-> true)
    element.readOnly = ""  (then .readonly-> false)
    

    【讨论】:

    • 在代码 sn-p 中提供您的答案。现在还不够清楚
    【解决方案5】:

    在我的例子中,我需要一个对象,我们只能设置它的属性一次。
    所以当有人试图改变已经设置的值时,我让它抛出一个错误。

    class SetOnlyOnce {
        #innerObj = {}; // private field, not accessible from outside
    
        getCurrentPropertyName(){
            const stack = new Error().stack; // probably not really performant method
            const name = stack.match(/\[as (\w+)\]/)[1];
            return name;
        }
    
        getValue(){
            const key = this.getCurrentPropertyName();
    
            if(this.#innerObj[key] === undefined){
                throw new Error('No global param value set for property: ' + key);
            }
    
            return this.#innerObj[key];
        }
    
        setValue(value){
            const key = this.getCurrentPropertyName();
    
            if(this.#innerObj[key] !== undefined){
                throw new Error('Changing global parameters is prohibited, as it easily leads to errors: ' + key)
            }
    
            this.#innerObj[key] = value;
        }
    }
    
    
    class GlobalParams extends SetOnlyOnce {
        get couchbaseBucket() { return this.getValue()}
        set couchbaseBucket(value){ this.setValue(value)}
    
        get elasticIndex() { return this.getValue()}
        set elasticIndex(value){ this.setValue(value)}   
    }
    
    const _globalParams = new GlobalParams();
    
    _globalParams.couchbaseBucket = 'some-bucket';
    _globalParams.elasticIndex = 'some-index';
    
    console.log(_globalParams.couchbaseBucket)
    console.log(_globalParams.elasticIndex)
    
    _globalParams.elasticIndex = 'another-index'; // ERROR is thrown here
    console.log(_globalParams.elasticIndex)

    【讨论】:

      猜你喜欢
      • 2010-11-14
      • 1970-01-01
      • 1970-01-01
      • 2016-09-20
      • 1970-01-01
      • 2021-03-24
      • 1970-01-01
      相关资源
      最近更新 更多