Is it an antipattern to set an array length in JavaScript?
Solution 1:
Does it have decent browser support?
Yes. This has been present since the very first edition of ECMAScript:
Specifically, whenever a property is added whose name is an array index, the length property is changed, if necessary, to be one more than the numeric value of that array index; and whenever the length property is changed, every property whose name is an array index whose value is not smaller than the new length is automatically deleted.
Standard ECMA-262, 15.4
In ECMAScript 5, this was moved to 15.4.5.2.
Attempting to set the length property of an Array object to a value that is numerically less than or equal to the largest numeric property name of an existing array indexed non-deletable property of the array will result in the length being set to a numeric value that is one greater than that largest numeric property name.
Standard ECMA-262, 15.4.5.2
All browsers support this behavior.
Do the removed values get garbage collected properly?
Yes. (See quotes above, and 15.4.5.1.)
Is it an antipattern to set array length in Javascript?
No, not really. While arrays are "exotic objects" in ES6-speak, the real crazily unique thing about arrays are their indexing, not setting the length
property. You could replicate the same behavior with a property setter.
It's true that property setters with non-subtle effects are somewhat unusual in JS, as their side-effects can be unobvious. But since .length
has been there in Javascript from Day 0, it should be fairly widely understood.
If you want an alternative, use .splice()
:
// a is the array and n is the eventual length
a.length = n;
a.splice(n, a.length - n); // equivalent
a.splice(n, a.length); // also equivalent
If I were avoid setting .length
for some reason, it would be because it mutates the array, and I prefer immutable programming where reasonable. The immutable alternative is .slice()
.
a.slice(0, n); // returns a new array with the first n elements
Solution 2:
Arrays are exotic objects.
An exotic object is any form of object whose property semantics differ in any way from the default semantics.1
The property semantics for arrays are special, in this case that changing length affects the actual contents of the array.
An Array object is an exotic object that gives special treatment to array index property keys [...] 2
The behavior of the specific property key for this array exotic object is outlined as well.
[...] Every Array object has a length property whose value is always a nonnegative integer less than 2^32. The value of the length property is numerically greater than the name of every own property whose name is an array index; whenever an own property of an Array object is created or changed, other properties are adjusted as necessary to maintain this invariant [...]
This section is detailing that the length property is a 32 bit integer. It is also saying that if an array index is added, the length is changed. What this is implying is that when an own property name that is not an index is used, the length is not changed and also that names which are not indexes are considered numerically less than the indexes. This means that if you have an array and also add a string (not implicitly numeric either, as in not "3") property to it, that changing the length to delete elements will not remove the value associated with the string property. For example,
var a = [1,2,3,4];
a.hello = "world";
a.length = 2; // 3 and 4 are removed
console.log(a.hello);//"world"
[...] Specifically, whenever an own property is added whose name is an array index, the value of the length property is changed, if necessary, to be one more than the numeric value of that array index; [...]
In addition to the truncation, expansion is also available by use of an index. If an index value is used (an integer basically) then the length will be updated to reflect that change. As arrays in JavaScript are sparse (as in, no gaps allowed) this means that adding a value for a larger index can make an array rather larger.
var a = [1,2,3,4];
a[50] = "hello";
console.log(a.length);//51
[...] and whenever the value of the length property is changed, every own property whose name is an array index whose value is not smaller than the new length is deleted.
Finally, this is the specific aspect in question which the OP raises. When the length property of an array is modified, it will delete every index and value which is numerically more than the new length value minus 1. In the OP's example, we can clearly see that changing the length to 2 removed the values at index 2 and 3, leaving only the first two values who had index 0 and 1.
The algorithm for the deletion mentioned above can be found in the ArraySetLength(A, Desc) definition.
- b. Let deleteSucceeded be
A.[[Delete]](ToString(oldLen))
. 3
Which is converting the index to a string and using the delete behavior for objects on the index. At which point the entire property is removed. As this internally reaches the delete call, there is no reason to believe that it will leak memory or even that it is an anti pattern as it is explicitly described in the language specification.
In conclusion, Does it have decent browser support? Yes. Do the removed values get garbage collected properly? Yes.
However, is it an anti-pattern? This could be argued either way. What it really breaks down to is the level of familiarity with the language of others who would be using the same code which is a nice way of saying that it may not have the best readability. On the other hand, since JavaScript does take up bandwidth or memory with each character used (hence the need for minification and bundling) it can be useful to take this approach to truncate an array. More often than not, worrying about this type of minutia is going to be a micro-optimization and the use of this special property should be considered on a case to case basis in context with the overall design of the related code.
1. The Object Type ECMA 6
2. Array Exotic Objects ECMA 6
3. ArraySetLength(A, Desc) ECMA 6
Solution 3:
As it is writeable https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length (seen here) It should be okay to use it that way
You can set the length property to truncate an array at any time. When you extend an array by changing its length property, the number of actual elements does not increase; for example, if you set length to 3 when it is currently 2, the array still contains only 2 elements.
There's even an example how to shorten an array, using this on Mozilla Developer Network
if (statesUS.length > 50) {
statesUS.length = 50;
}