0

I'm trying to proxy the [[set]] method on JavaScript arrays. Is it possible to proxy all arrays, and not just a specific one? My handler would check the object being set in the array and if it hasOwnProperty('xyz') then console.log it.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy says that I would do that with something like

set(target, prop, val) { // to intercept property writing
    if (val.hasOwnProperty('xyz') {
        console.log(prop);
    };
    target[prop] = val;
    return true;
  }

However, Proxy seems to only work on single objects. Given that there is no prototype [[set]], how can I proxy this operation for every array?

Ideally, it would work something like this:

Array = new Proxy(Array, {
set(target, prop, val) { // to intercept property writing
    if (val.hasOwnProperty('xyz') {
        console.log(prop);
    };
    target[prop] = val;
    return true;
  }
});

let obj = {xyz: 1}
let list = [];
list[0] = obj; // would log 1

I tried the above code snippet, but it does not redefine the method for every array. One possible method could be to proxy Array.prototype.constructor to return an array with the [[set]] pre-proxied to have the check for the value.

Thanks!

New contributor
enbyte is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
2
  • No, that’s not possible, and it seems like it would be a bad thing if it were possible. What were you going tot use this for?
    – Ry-
    Commented 18 hours ago
  • "Is it possible to proxy all arrays, and not just a specific one?" - no. Why would you want that?
    – Bergi
    Commented 17 hours ago

1 Answer 1

-1

This code actually does not replace all Arrays... You can't actually do that. Also, when you cast [] then JavaScript will always use the native Array over the Array defined in your context...

There's another bad-side in putting your Proxy over your array prototype because it will actually be under the actual array object. And you need it to be over your object, not under it.

Thus, the only solution is to make a function like the above that will construct a fully functional proxied array.

/* Keep `Array` as is. It's better this way. */
function MyHandledArray ()
{
   /* Your proxy should go over your object, not it's prototype. */
   return new Proxy
   (
      /* This just use the `Array` constructor to build a new object. */
      Reflect.construct(Array, arguments, MyHandledArray),
      /* Bellow is the handler that will intercept the new object. */
      {
         set: ( Target, Property, Value ) =>
         {
            if ( ( typeof Value == 'object' ) && Reflect.has(Value, 'xyz') )
            {
               /* You want to log the indexes which contains objects with xyz property? */
               // console.log(Property);
               /* Or you wanted to log the `xyz` property value of the `Value` object? */
               console.log(Value.xyz);
            }
            
            if ( typeof Target == 'object' )
            {
               Target[Property] = Value;
            
               return Reflect.has(Target, Property);
            }
            
            return false;
         }
      }
   );
}

/* Adjust the function to properly inherit `Array`... */
Reflect.setPrototypeOf(MyHandledArray, Array);
Reflect.setPrototypeOf(MyHandledArray.prototype, Array.prototype);

/* Then, only then, use it. */

var Obj = {xyz: 1};
var Arr = MyHandledArray();

Arr[0] = Obj; // outputs: 1
Arr[1] = {xyz: 2}; // outputs: 2
Arr[3] = {nothing: null}; // outputs nothing
Arr[4] = {xyz: 'Hello', more: 'stuff'}; // outputs: Hello
/* It will work in all inserting methods too. */
Arr.push({xyz: 'World', more: 'stuff'}); // outputs: World

/* Note that the defined proxy does not verify the contents set through constructions... */
var NewArr1 = MyHandledArray({xyz: 'This is not logged'});
var NewArr2 = MyHandledArray.from(Arr);


NOTE: Neither your question or your example required to verify the construction parameters... You can just add the following at the start of the function if needed:

for ( const Argument of arguments )
{
   if ( ( typeof Argument == 'object' ) && Reflect.has(Argument, 'xyz') )
   {
      console.log(Argument.xyz);
   }
}  
0

Not the answer you're looking for? Browse other questions tagged or ask your own question.