1

When defining an array as a property of an ES5-style object, I want to make it so the property's value cannot be changed.

'use strict';

var global = Object.create(Object.prototype, {
    names: {
        value: ['Barney', 'Trogdor'],
        writable: false
    }
});

global.names.push('Jackson'); // I expected a read-only error here

console.log(global.names[2]); // >> Jackson

global.names = ['Ooga', 'Booga']; // >> TypeError: "names" is read-only

It seems that I have only protected against property assignment.

Is there any way to protect against stuff like Array.push() that modifies my "unwritable" array?

Jackson
  • 9,188
  • 6
  • 52
  • 77

2 Answers2

3

Object.seal() seems to work.

'use strict';

var global = Object.create(Object.prototype, {
    names: { value: Object.seal(['Barney', 'Trogdor']) }
});
global.names.push('Jackson'); // >> TypeError: global.names.push(...) is not extensible

EDIT: Actually, nevermind.

In the previous example, were I to append the following line of code:

global.names[0] = 'Jackson'; // Still works

I believe Object.freeze() was what I actually wanted.

var global = Object.create(Object.prototype, {
    names: { value: Object.freeze(['Barney', 'Trogdor']) }
});
global.names.push('Jackson');  // >> TypeError: global.names.push(...) is not extensible
global.names[0] = 'Jackson';   // >> TypeError: 0 is read-only
global.names = ['Jackson'];    // >> TypeError: "names" is read-only
Jackson
  • 9,188
  • 6
  • 52
  • 77
  • I believe `Object.create` is unnecessary here then, `var global = { names: Object.freeze(['Barney', 'Trogdor']) }` should suffice. – Fabrício Matté Dec 09 '13 at 03:17
  • Nope. When I define the `names` property with `Object.create()` the property is implicitly `writable: false` (I only explicitly stated it in my question code for clarity). The property becomes `writable` if I use the object literal syntax. Even so, I was planning on adding more properties to `global` anyway. – Jackson Dec 09 '13 at 04:08
  • Oh makes sense, that way `global.names = []` will fail. I did +1 your answer, though I usually tend to keep things permissive e.g. use a full capitals name to denote constants and the developer will implicitly know that modifying such properties may yield an unexpected outcome. – Fabrício Matté Dec 09 '13 at 04:13
  • Yeah, the array *is* basically a constant now. `global.NAMES` would have been a better name. – Jackson Dec 09 '13 at 04:20
-2

Once all the data is loaded, why not overwrite the push method on what you have:

global.names.push = function () {return false}
C. S.
  • 805
  • 6
  • 6
  • 1
    I would also need to overwrite `.splice()`, etc. This is an unfavorable solution. – Jackson Dec 08 '13 at 09:26
  • True, but Object.seal(), although a great solution, [with regards to IE] is only applied to version 9+ I would like to know a proper solution for this. Perhaps a fallback method if this method is not supported would be to overwrite them or only providing read only access to the array. – C. S. Dec 08 '13 at 10:20
  • I was asking this question in the context of ES5, which implied IE9+. I do not need a fallback for `Object.seal()`; actually, `.seal()` doesn't feel very "semantic" to me given that `writable: false` exists. – Jackson Dec 08 '13 at 10:54