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?
You can set the value of the length property to change the size of an array. If you set length to be smaller than its previous value, the array is truncated, and elements at the end are lost.
The JavaScript array length property states the number of items in an array. To find the length of an array, reference the object array_name. length. The length property returns an integer.
Parameters. The object on which to define the property. The name or Symbol of the property to be defined or modified. The descriptor for the property being defined or modified.
According to ECMAScript 15.4.5.1, 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"
, thena. If the
[[Value]]
field of Desc is absent, theni. 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 15.4.5.2 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.
"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.
var a = ['a','b'];
a.push = function(x){
console.log('added: ',x,', length changed: ',(this.length+1));
this[this.length]=x
}
a.push('c');
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With