Freezing object properties or array values, whilst keeping them extensible
Object.defineProperty(myArray, "length", {writable: false});
A writable, non-configurable property can still be set to non-writable.
Le 04/06/2013 06:57, Andy Earnshaw a écrit :
Something that occurred to me today is that we have methods for locking down objects in different ways except for preventing changes to existing properties. We have:
- Object.seal to prevent new properties being added or existing properties being deleted
- Object.preventExtensions to prevent new properties being added
- Object.freeze to prevent new properties being added or existing properties being altered/deleted
Let's say, though, that my library has a function that accepts an object as an argument, performs some changes to the values of properties on that object and returns it to the developer's code for use in a different function provided by my library. If I didn't want the developer changing any of the properties the function sets, I'd have to copy all properties across to a new object.
If you didn't create the object yourself, you're subject to the decisions of the provider when it comes to guaranteeing something about an object. You can try to add your own stuff, but it's up to the provider to decide whether they let you do that. I would recommend using a WeakMap for this type of use case (attaching information to an object you didn't create).
That's all well and good, but what if it's an array? The length property of an array isn't configurable, so you can't prevent it being written without freezing it which, in turn, prevents extensions altogether.
Object.defineProperty(a, 'length', {writable: false}) should work. Firefox had a long-standing bug making it non-compliant on this... which has been fixed recently [1] and will be part of Firefox 23.
Array.create might have taken care of that, depending on how it was implemented, but it seems as though that's been deferred in favour of the <| operator proposal.
I'm not sure I understand what you're saying here. Do you want to return the same or a different object? For sure, your caller will know if you returned a different object (because it can compare the argument and the return value)
If it's not too late for ES6 proposals, I'd like to suggest that we add either an function to freeze only existing properties of an object, e.g. Object.lock(), or a function to freeze arrays, e.g. Array.freeze(). The latter would only freeze the length property and existing array index properties, keeping it extensible.
Your use case suggests the need for a well-encapsulated weakmap, no need for new constructs, I think. We have enough low-level control over object I feel.
David
Object.defineProperty(a, 'length', {writable: false}) should work. Firefox had a long-standing bug making it non-compliant on this... which has been fixed recently [1] and will be part of Firefox 23.
Ah, that explains why it wasn't working then. I tried it, it didn't work so I searched and found nothing and I assumed it was just how things were.
I'm not sure I understand what you're saying here. Do you want to return the same or a different object? For sure, your caller will know if you returned a different object (because it can compare the argument and the return value)
Well, the plan was for whatever worked, really. If I could have created a new array that I had more control over, then that would simply be the standard behaviour for that function.
Your use case suggests the need for a well-encapsulated weakmap, no need
for new constructs, I think. We have enough low-level control over object I feel.
A weakmap wouldn't have really done the trick because the external code could have modified the contents of the array before passing it back. The weakmap would help in identifying a previously prepared array, sure, but it wouldn't keep the integrity of it intact. However, it seems that all this is pretty much a moot point, Array.freeze would just be an easier way to do it than iterating over the array and changing all the elements to {writable:false, enumerable:true, configurable:false} as well, but I can understand not wanting to add simple stuff like this in.
So... carry on.
Le 04/06/2013 11:24, Andy Earnshaw a écrit :
I'm not sure I understand what you're saying here. Do you want to return the same or a different object? For sure, your caller will know if you returned a different object (because it can compare the argument and the return value)
Well, the plan was for whatever worked, really. If I could have created a new array that I had more control over, then that would simply be the standard behaviour for that function.
In ES6, you can create a proxy.
Your use case suggests the need for a well-encapsulated weakmap, no need for new constructs, I think. We have enough low-level control over object I feel.
A weakmap wouldn't have really done the trick because the external code could have modified the contents of the array before passing it back.
The external code that gave you the array or one you give the array to? In a nutshell, whoever creates something has initially all power over it and can decide to share full or partial (or no) authority. Can you provide more details about your use case?
The weakmap would help in identifying a previously prepared array, sure, but it wouldn't keep the integrity of it intact. However, it seems that all this is pretty much a moot point, Array.freeze would just be an easier way to do it than iterating over the array and changing all the elements to {writable:false, enumerable:true, configurable:false} as well, but I can understand not wanting to add simple stuff like this in.
Indeed. A concern is where to draw the line between what's in the language and what isn't. In any case, nothing stops you from adding Array.freeze yourself... but please don't add it to the built-in Array ;-)
Something that occurred to me today is that we have methods for locking down objects in different ways except for preventing changes to existing properties. We have:
Let's say, though, that my library has a function that accepts an object as an argument, performs some changes to the values of properties on that object and returns it to the developer's code for use in a different function provided by my library. If I didn't want the developer changing any of the properties the function sets, I'd have to copy all properties across to a new object. That's all well and good, but what if it's an array? The length property of an array isn't configurable, so you can't prevent it being written without freezing it which, in turn, prevents extensions altogether.
Array.create might have taken care of that, depending on how it was implemented, but it seems as though that's been deferred in favour of the <| operator proposal.
If it's not too late for ES6 proposals, I'd like to suggest that we add either an function to freeze only existing properties of an object, e.g. Object.lock(), or a function to freeze arrays, e.g. Array.freeze(). The latter would only freeze the length property and existing array index properties, keeping it extensible.
Other than introducing another aspect of control over the library user, I could see this being useful as an optimisation; if the array elements are locked and you can confirm it was locked by your code then you could make assumptions about the last element in the array based on an inspection of the first, eliminating the need for a loop.