Take the 2-minute tour ×

I would like (mainly for academic reasons) to be able to set an accessor on an array's length using Object.defineProperty(), so than I can notify for size changes.

I am aware of ES6 object observe, and watch.js, but I would like to attempt to do this in ES5 without extra libraries if possible, even if this is only for V8/Chrome.

A sample array:

var demoArray = ['one', 'two']

Alas Chrome, out of the box, makes length not configurable:

Object.getOwnPropertyDescriptor(demoArray, 'length')
Object {value: 2, writable: true, enumerable: false, configurable: false}

And it doesn't work:

Object.defineProperty(demoArray, 'length', { set: function(){ console.log('length changed!')} })

Fails with 'TypeError: Cannot redefine property: length'

As you can see, configurable is false - so the failure is understandable. However according to MDN it should be possible.

How can I get defineProperty working on an array's length property? Should this work?

share|improve this question
I think the MDN page (and the links from there) only refer to redefining the writable property descriptor (and maybe the value one), but changing the data property to an accessor property is definitely prohibited. –  Bergi Sep 5 '13 at 15:37
I made an edit (after you accepted) that might clear up the MDN passage: it seems to be talking about a bug which blocks setting the value using defineProperty, which should be possible according to the spec, but is some implementations is sometimes blocked erroneously (as if it incorrectly had writable: false). However, defining a setter is right out, in compliance with the spec. –  apsillers Sep 5 '13 at 15:38
Yeah whereswalden.com/2013/08/05/… talks about the same thing. Setting a data property works fine: Object.defineProperty(demoArray, 'length', {value: 0}); but setting an accessor property won't work and breaks the spec. –  mikemaccana Sep 5 '13 at 15:58
Just so people are aware, I updated the MDN page slightly to be clearer about what is/isn't allowed. Look at page history if you want the text that was there when this question and answers were written. length being redefinable doesn't mean you get a pass on all the other restrictions Object.defineProperty ordinarily enforces. Specifically: until you change length's writability to false, you can only change length's writability and value. You can't change the property to a getter/setter pair, ever. –  Jeff Walden Sep 6 '13 at 2:09
add comment

4 Answers

up vote 1 down vote accepted

Since reading a little more about this, Kangax's excellent article dedicated to the topic of subclassing Array covers a a variety of techniques. One technique, called Array prototype injection is used to subclass Array in popular libraries like Ractive.js. It relies on the non-spec, but popular __proto__ being exposed, but does allow 'accessors' on length.

share|improve this answer
add comment

"Adding a event listener to Array.length will cause a huge impact on overall performance of your application, and you should avoid it by all means" @Juno;

"don't rely on redefining the length property of an array to either work, or to work in a particular manner" MDN;

Since we can't touch length, to have a similar behavior we can change the push method and aways use it to add new values to the array.

a.push = function(x){
  console.log('added: ',x,', length changed: ',(this.length+1)); 
share|improve this answer
add comment

According to ECMAScript, arrays have their own [[DefineOwnProperty]] internal method, so the configurable: false is not necessarily an immediate deal-breaker. An early step in this method says:

3. If P is "length", then

  a. If the [[Value]] field of Desc is absent, then

     i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", Desc, and Throw as arguments.

Therefore, if you don't have a value attribute in your property descriptor, the property setting operation is delegated to the default [[DefineOwnProperty]] method. ECMAScript requires that the length property have configurable: false, so the default method will fail.

If you do set value, to avoid dropping into the default method, you cannot also define a setter. Attempting to do so will cause an error in Chrome (or any browser in compliance with section 8.10):

TypeError: Invalid property. A property cannot both have accessors and be writable or have a value

Therefore, it seems impossible to define a setter on array length in any ES5-compliant implementation.

Note that the MDN article seems to be talking about browsers erroneously refusing to set the value using defineProperty, which should be generally possible, but sometimes is not, due to a bug.

share|improve this answer
Thanks for the comprehensive answer. Would it be correct to say this essentially comes down to .length being both a data descriptor and an accessor descriptor? I've read some similar stuff here: whereswalden.com/2013/08/05/… –  mikemaccana Sep 5 '13 at 15:45
@nailer Mostly it just comes down to configurable: false. Array's custom [[DefineOwnProperty]] allows data descriptors with value. When you try to define an accessor descriptor (naturally, without value), the custom method throws you to the default method, which immediate stops you, because you're trying to violate configurable: false. The custom method doesn't enforce the configurable: false requirement, but the custom method only allows setting data descriptors with a value item. –  apsillers Sep 5 '13 at 15:50
add comment

As it clearly say on the document:

Only Internet Explorer 9 and later, and Firefox 23 and later, appear to fully and correctly implement redefinition of the length property of arrays. For now, don't rely on redefining the length property of an array to either work, or to work in a particular manner. And even when you can rely on it, there's really no good reason to do so.

It is not supported by all browsers including Chrome, and you should find another way to do what you desire to do in the first place without changing length property of Array.

Once, a property is defined with configurable: false, there is really no way to change its configurations.

So in this case, it in not possible. And even if it is, it will have performance issues because Array.length is used everywhere by all libraries, is accessed so often, and is constantly changing everywhere.


share|improve this answer
Ah, I'd missed the 'only' part of the doc I referred to. As mentioned, though, I'm not planning to monitor all objects that inherit from Array, just a single array. –  mikemaccana Sep 5 '13 at 15:30
add comment

Your Answer


