58

Is it bad to use code like:

var a = [1,2,3,4];
a.length = 2; // 3 and 4 are removed

Does it have decent browser support? Do the removed values get garbage collected properly?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Fluffy
  • 27,504
  • 41
  • 151
  • 234
  • possible duplicate of [How to empty an array in JavaScript?](http://stackoverflow.com/questions/1232040/how-to-empty-an-array-in-javascript) – levi Jul 21 '15 at 19:07
  • 6
    @levi Is that really a duplicate? It is definitely related, but that's not really what the OP is asking. – Josh Crozier Jul 21 '15 at 23:17
  • 7
    I recommend changing the title of the question. "Is it an antipattern" is an entirely different question than the two you stated in the text of your question, and whether something is an antipattern or not is subjective and probably not an appropriate question for Stack Overflow for that reason. – user2752467 Jul 22 '15 at 01:38
  • 1
    @Justin Lardinois, what would be a better title? – Fluffy Jul 22 '15 at 08:01
  • 1
    I believe this question belongs to [codereview](http://codereview.stackexchange.com/) – Panther Jul 22 '15 at 10:36
  • 1
    @Fluffy a better title would be "Does manipulating array length in Javascript have ill effects?" – user2752467 Jul 27 '15 at 19:12

7 Answers7

49

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
Paul Draper
  • 78,542
  • 46
  • 206
  • 285
  • 1
    The `deleteCount` parameter for the `splice()` function is optional. If omitted, all elements from index n will be removed. So simply calling `a.splice(n)` (without a second parameter) will have the same effect as `a.length = n` – Waruyama Sep 22 '22 at 10:43
33

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.

  1. 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

Travis J
  • 81,153
  • 41
  • 202
  • 273
  • 2
    1+ Nice answer. Hopefully it get some views now that the question is on the "Hot Network Questions" list. – Josh Crozier Jul 21 '15 at 23:18
  • For those arguing that it is an antipattern, what (semantically equivalent) would they suggest to use instead? – Bergi Jul 22 '15 at 04:13
  • "_As arrays in JavaScript are sparse (as in, no gaps allowed)_ ..." You meant _dense_ ? But we know JS arrays are sparse... – Ciprian Tomoiagă Jul 08 '17 at 21:55
14

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;
}
Hendry
  • 882
  • 1
  • 11
  • 27
  • 3
    That example is silly; the `if` check is totally unnecessary! – Seiyria Jul 21 '15 at 23:24
  • 8
    @Seiyria: not if there's the possibility of also handling lists of US states less than 50 states long (e.g. if dealing with historical data from when the US had less than 50 states, or just handling some subset of states). In that case, if you just set the length to 50 without the check, you'd end up with empty junk entries at the end. – Mac Jul 21 '15 at 23:45
  • How does garbage collection work in this case? What happens to the objects that are outside the bounds of the new array? – Plasma Jul 22 '15 at 01:09
  • @Plasma: They are no more referenced by those deleted properties, and if that makes them eligible to be garbage-collected they will be just as normal. – Bergi Jul 22 '15 at 04:10
  • I can't think of any reason why you'd want to use that code example, but at least it demonstrates the feature being discussed. – user253751 Jul 22 '15 at 06:14
  • 1
    @Mac According to the quote in this answer, no, you wouldn't, would you? If you have an array of length 40, and set its length to 50, then according to this answer, the array still has only 40 elements. –  Jul 22 '15 at 07:37
  • 1
    [unrelated to question] Be very wary of the example given. If you've a list with a number of valid values (in this case, 50 valid values, as there are 50 states), truncating the array is a REALLY BAD way to ensure that you have legal values. Say you' are grouping addresses by state, and want to ignore addresses from Washington DC and Guam and various other territories, allowing only the 50 states. In that case, run through your array, swapping each invalid entry to the end as you find it, and truncating by one. Truncating alone will throw away legal values, and keep bad ones. – Dewi Morgan Jul 22 '15 at 07:59
  • 1
    @hvd but if you iterate over the thing after the mischief you will find undefined extra stuff – mccc Jul 22 '15 at 11:01
  • @mccc That depends on how you iterate. If you increase the index until you reach `length`, then yes. If you check `Object.keys(array)`, then no. –  Jul 22 '15 at 14:23
  • @hvd: the comment by @mccc illustrates **exactly** the kind of problem I was referring to. While iterating over an array using `Object.keys` might have its uses (e.g. sparse arrays), the problem described fundamentally breaks the 99.99% of cases where iteration with an index variable is more appropriate and *should* work. – Mac Aug 12 '15 at 00:10
  • @Mac I understand that. My point us that even if it is a real problem, your description of what happens does not match what actually happens. It is possible to point to the problem more accurately, which would give the same warning to readers, but would not be misleading them and would not be contradicting this answer –  Aug 12 '15 at 06:25
11

It's better to use slice because your intentions are clear.

var truncated = a.slice(0,2)

Slice does create a new array though.

If that is an issue for you then there is nothing wrong with modifying the length property.

It's actually the fastest way of truncation and supported in all browsers. jsperf

Miguel Mota
  • 20,135
  • 5
  • 45
  • 64
  • 1
    `.slice` creates a new array though. – Felix Kling Jul 21 '15 at 19:02
  • 3
    I suppose you're suggesting the pattern of usage `a = a.slice(0,20)`? But `Array.prototype.slice` is intended for copying, and it's not outrightly clear that it can be used to achieve deletion of elements. Just my own opinion. – Igwe Kalu Jul 21 '15 at 19:02
4

Is it an antipattern to set array length in JavaScript?

No. It would be OK to set it to truncate the array.

Does it have decent browser support?

Yes, see the compatiblity matrix here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length

Do the removed values get garbage collected properly?

Yes, they should. Will they actually get gced - you will need to profile the JavaScript code if this is a real concern.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
6ton
  • 4,174
  • 1
  • 22
  • 37
2

This way of truncating an array is mentioned in JavaScript: Good Parts, so it is not an anti-pattern.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Pol
  • 5,064
  • 4
  • 32
  • 51
1

It is not an anti pattern. It is supported in all major browsers. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length

abs
  • 801
  • 6
  • 15