2

I'm working on a code similar to the following snippet:

let arr = [];
let arr_handlers = {
    set: function(target, property, value, receiver) {
        console.log('setting ' + property + ' for ' + target + ' with value ' + value);

        target[property] = value;
        return true;
    }
};
let arr_proxy = new Proxy(arr, arr_handlers);

arr_proxy.push(1);
// setting 0 for  with value 1
// setting length for 1 with value 1
arr_proxy[1] = 5;
//  setting 1 for 1 with value 5
console.log(arr.length);
// 2

The problem is that proxy behaves differently when I use arr_proxy.push() and when I set value through index:

Why proxy setter isn't invoked to set length when array value is set by index?

aryndin
  • 591
  • 5
  • 18
  • 1
    Your array has only one item, but you're setting an item on index 3. That converts the array into a sparse array, which doesn't update the length. I can't find this in the spec though. –  Mar 03 '20 at 19:31
  • @Amy if I set an item on index 1, it behaves the same way. I've updated the question – aryndin Mar 03 '20 at 20:04
  • 1
    Yeah, my analysis/conclusion was wrong. I'm not sure. I'll leave my comment up so someone else doesn't come along and say the same thing. –  Mar 03 '20 at 20:10

1 Answers1

0

Why proxy setter isn't invoked to set length when array value is set by index?

Because that dynamic length property that is special to arrays exists only on the target, inside your proxy.

In contrast, the push method does set the .length property on its receiver explicitly. It is not automatically triggered by the assignment to the index. You can try with

const obj = { length: 0, push: Array.prototype.push };
obj.push(5)
console.log(obj) // { length: 1, 0: 5 }

You can achieve the same behaviour as the arr_proxy.push(5) call by writing

const _len = arr_proxy.length;
arr_proxy[_len] = 5;
arr_proxy.length = _len + 1;
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • But the question is _why_ setter is not automatically triggered when assigning to the index? `length` is _special to arrays exists only on the target_ in both cases, do I add value through `push` or through index, but by some reason it is set by wrapper only when assigning through `push()` – aryndin Mar 03 '20 at 20:41
  • @aryndin Push does not only add the value. Push also explicitly writes to the `.length` property (even if that's unnecessary on array objects, which already will have the right length). – Bergi Mar 03 '20 at 21:11
  • @aryndin The `length` setter is not triggered during assignment to `[1]` because you're not assigning to `.length`. The `length` *getter* will then recompute the current length afterwards when you *access* the `.length` property. – Bergi Mar 03 '20 at 21:12
  • isn't `push()` [array prototype specific](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push) and thus should take into account that it is not necessary to explicitly set length for arrays? – aryndin Mar 04 '20 at 11:36
  • @aryndin No, the `push` method [is generic](https://stackoverflow.com/q/29835765), it doesn't require an array - see the example in my answer with `obj`, which does not have a magic `.length` and still works. (I'm not saying that engines don't implement internal optimisations for `push` when it's called on an array, but it cannot take advantage of them when it's called on an arbitrary proxy object of course). Also notice that `push` needs to be generic to work on your proxy at all, if you tried a proxy around a typed array it would [throw exceptions](https://stackoverflow.com/q/43236329). – Bergi Mar 04 '20 at 12:35