JavaScript: use accessProperty accessor on .length array?

I would like (mainly for academic reasons) to set the accessor on the length array using Object.defineProperty() , so how can I notify about size changes.

I am aware of the observation of the ES6 object and watch.js, but I would like to try to do it in ES5 without additional libraries, if possible, even if it is only for V8 / Chrome.

Array example:

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

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

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

And this does not work:

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

Crash with 'TypeError: Cannot redefine property: length'

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

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

+6
source share
5 answers

Since I read a little more about this, Kangax's excellent Array subclass topic covers a lot of methods. One method, called Entering an Array Prototype , is used to subclass Array in popular libraries such as Ractive.js. It relies on a nonspecific, but popular __ proto __ , which is available but allows โ€œaccessorsโ€ in length.

+1
source

According to ECMAScript 15.4.5.1 , arrays have their own internal method [[DefineOwnProperty]] , so configurable: false not necessarily an immediate -breaker deal. At an early stage of this method it says:

3. If P is "length" , then

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

I am. Return the result of calling the default [[DefineOwnProperty]] built-in method (8.12.9) in the missing "length" , Desc, and Throw as arguments.

Therefore, if you do not have the value attribute in the property descriptor, the property installation operation is delegated by default [[DefineOwnProperty]] . ECMAScript 15.4.5.2 requires that the length property has configurable: false , so the default method will not be executed.

If you set value to avoid using the default method, you also cannot define the setter. Attempting to do this will result in an error in Chrome (or any browser according to 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 the setter in the length array in any ES5-compatible implementation.

Note that the MDN article seems to suggest that browsers mistakenly refuse to set value with defineProperty , which should be generally possible, but sometimes this is not the case due to an error.

+1
source

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

โ€œDo not rely on overriding the array length property to work or to work in a specific wayโ€ MDN;

Since we cannot touch the length to have similar behavior, we can change the push method and 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'); 
+1
source

rmprop.js allows the array proxy so you can do whatever you want with the .length property:

 var rmprop = require('rmprop'); var arr = rmprop(['my','array']); // instead, length will return sum of lengths of all elements Object.defineProperty(arr, 'length', { get: function() { return this[unprop.real].join('').length; }, }; arr.length; // 7 
+1
source

As the document clearly states:

Only Internet Explorer 9 and later and Firefox 23 and later will appear. fully and correctly implement the redefinition of the length property of arrays. For now, don't rely on overriding the length property of an array to work or to work in a specific way. And even when you can rely on it, there really is no good reason for this.

It is not supported by all browsers, including Chrome, and you should find another way to do what you want, first of all, without changing the length property for the array.

Once a property is defined using configurable: false , it's really impossible to change its configurations.

So in this case it is impossible. And even if so, it will have performance problems, because Array.length used everywhere by all libraries, it accesses them so often and constantly changes everywhere.

Adding an event listener to Array.length will have a huge impact on the overall performance of your application, and you should avoid it by all means.

0
source

Source: https://habr.com/ru/post/953243/


All Articles