1

I'd like to be able to do something like

foo.x = 7;

And then do something to make that property read only so that no other code can change it. IE i'd like

foo.x = 10; //should do nothing

To do nothing is there anyway to do that in JS?

asutherland
  • 2,849
  • 4
  • 32
  • 50
  • Check this out: http://stackoverflow.com/questions/2428409/how-can-you-make-a-variable-object-read-only-in-javascript It might be helpful – MikeB Oct 04 '12 at 15:36

4 Answers4

5

With ECMAScript5 you can achieve that:

var foo = {};

Object.defineProperty(foo, 'x', {
    value: 7,
    writable: false,
    configurable: false,
    enumerable: true
});

You might want to check out MDN JavaScript for ECMAScript5 stuff.

benqus
  • 1,119
  • 2
  • 10
  • 24
1

I think this is what you're looking for. Check the example under Writable attribute

It only works in browsers that offer ECMA5 support, though. For older browsers, you can't define a non-writeable propery, but you can set a private variable which can be used with a getter:

var foo = (function(x)
{
    var getX = function()
    {
        return x;
    };
    getX.valueOf = function()
    {
        return this();
    };
    return {x:x,getX:getX};
})(7);//pass constant value
foo.x === 7;//true
foo.x = 5;//will work
//BUT:
foo.getX();//returns 7, always
foo.getX;//returns 7, so can be used as though it weren't a method
//WARNING:
foo.getX = 213;//x is lost forever...
Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
  • foo.getX return the function itself... But this doesn't really make sense... You want to hide and 'secure' that value, not expose it after you've done that... – benqus Oct 04 '12 at 15:59
  • 1
    @benqus: `foo.getX` doesn't return the function itself, it implicitly invokes `getX`'s `valueOf` method, which invokes `getX`. Provided, of course, you use it in such a way that a function reference needs to be coerced like [in this fiddle](http://jsfiddle.net/VeM4J/). The question was, AFAIK, how the have an accessible property that cannot be redefined. The initial value of `x` is exposed, but cannot be redefined as such. The worst that can happen is loosing the reference by reassigning the getter method, but `x` can't be changed – Elias Van Ootegem Oct 04 '12 at 16:04
  • @benqus: Besides, that's just a hacky way to mimic an invariable property on an object for browsers that don't support `Object.defineProperty`, first part of my answer links to the MDN (ECMA5 only) docs. I just added the code for completeness. – Elias Van Ootegem Oct 04 '12 at 16:08
  • Sure it is a hacky way, adding the foo.getX to a sitring literal will "fetch" the the value via the .valueOf() method. Indeed, it is cool, but seems a little less maintainable than ES5 stuff... Now that IE6 is literally dead, we have our "wings" open. =) – benqus Oct 04 '12 at 16:13
  • After 6 comes 7 and 8, not 9... and it's important to remember that, sadly, IE8 [still makes up for just over 13% of all browsers](http://gs.statcounter.com/#browser_version-ww-monthly-201109-201209) so don't cheer just yet, even IE7 hasn't dipped below the 1% mark – Elias Van Ootegem Oct 04 '12 at 16:16
  • I used to go based on this table: http://kangax.github.com/es5-compat-table/ and forget IE. Sorry 'bout that, it just doesn't worth the efforts... =) – benqus Oct 04 '12 at 16:19
  • I don't _like_ dealing with IE, but in a professional environment, you have to respect every customers decision, even if they choose to use IE8. If they're _that_ moronic, chances are they'll buy your product and will be willing to pay twice as much anyway – Elias Van Ootegem Oct 04 '12 at 16:21
  • In a professional environment, you can suggest what environment the user should use and train them to be able to use. If customers doesn't like it, you should respect their decision. It is just a little weird since we believe that profit invert this process. =) But it's not. =) If you display a kind error page that his/her browser suuuucks, he/she will understand it. =) Of corse, morons are everywhere. Yes, they are loud, but they are few. =) – benqus Oct 04 '12 at 16:32
  • I'll blame youthful optimism, bordering on naivety for your beliefs. A lot of customers don't like their supplier telling them they made the wrong choice, that's a consultant's job. That, too, is why a consultant's pay is discussed (and sometimes even in part payd) up front. – Elias Van Ootegem Oct 04 '12 at 16:37
  • We are talking about browsers, dude, free softwares... There is no ROI on them... – benqus Oct 04 '12 at 16:40
  • I'm not, I'm talking about web services a company offers, their site is supposed to generate a healthy revenue. They don't want to belittle any of their costumers, and rightly so. If I buy a new piece of hardware, and the vendor doesn't provide a Linux driver, I feel disrespected as a client. I'm not going to change to FreeBSD, I'm going to buy my hardware elsewhere. – Elias Van Ootegem Oct 04 '12 at 16:53
  • 1
    Let's finish this since we are far off the original subject. =) – benqus Oct 04 '12 at 16:58
0

You can use Object.defineProperty to do this.

Object.defineProperty(foo, "x", {writable:false})

See this Fiddle

If you want to make an whole Object unwritable, you could also use the Objects freeze method

Object.freeze(foo);

Moritz Roessler
  • 8,542
  • 26
  • 51
0

Well, for backwards compatibility with old browsers you may need to define accessor functions. Alternatively you can use ready-made functionality provided by various frameworks (e.g. 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.

- Tengiz

Tengiz
  • 8,011
  • 30
  • 39