2

I was looking around online and I saw someone do something similar, Array.prototype.push was proxied and every push was handled, I want to do something like this:

new Proxy(Array.prototype.push, handler);

const array = [];

array.push("e"); // logs "e" to the console somehow from the handler

How can this be achieved?

6
  • 1
    Are you sure you need to use a proxy?, are you wanting everywhere you you create an array and call push you get logged, if so you just override the prototype, ps. Be careful doing this though, altering prototypes is a little bit frowned on. Commented Mar 1, 2023 at 14:56
  • 1
    This sounds like an XY problem. What do you want to achieve which requires proxying a built-in method? Commented Mar 1, 2023 at 14:56
  • @VLAZ I'm making a script that needs to be injected into a website, I'm trying to access an object that has certain attributes, I'm trying to proxy Array.push so I can do checks on each push and see if the push was done on the object I'm looking for, and if it was, save the object. Commented Mar 1, 2023 at 14:58
  • 1
    You can't use a proxy to intercept existing arrays, proxies work by wrapping an existing object and then all access is done via the proxy. Like I mentioned in first comment you can override the array protototype, but be very very careful when doing this. Commented Mar 1, 2023 at 15:00
  • 1
    @Keith is right. The only way to somehow get things to work as described is to overwrite the built-in global array. Which is usually to be avoided. Even if you were to modify it, at that point it wouldn't matter whether you used a proxy or not as you're already intrusively changing the built-in global. Proxies are transparent only when you get a chance to hand over a proxy instead of the object. Then it's the same to the consumer. But making sure all consumers use a proxy of an object you don't control is not that. And when built-ins are involved you best thread carefully. Commented Mar 1, 2023 at 15:05

4 Answers 4

5

This will work:

const handler = {
  apply(target, thisArg, argumentsList) {
    console.log(`called push with argument:', ${argumentsList}`);
    return Reflect.apply(target, thisArg, argumentsList)
  }
};

Array.prototype.push = new Proxy(Array.prototype.push, handler);


const a = []

a.push(1)

More info:

MDN article about Proxy

handler.apply

But it can break things so be careful

Sign up to request clarification or add additional context in comments.

7 Comments

Using a proxy here is pointless, if your going to override the prototype, then do it there, the proxy has gained nothing.
@Keith I tried to do that without a proxy and it caused issues, but maybe I did something wrong
You just need to remember the old method, -> const old = Array.prototype.push; Array.prototype.push = function (...args) { console.log(...args); old.call(this, ...args);}
This is the sort of thing I was looking for, appreciate it.
@bread Yes, but just remove the proxy, it's not doing anything, apart from making the push slower.
|
2

You need to proxy the array and call methods from the proxy.

// Based on:
// - https://stackoverflow.com/a/35610685/1762224
// - https://stackoverflow.com/a/47911346/1762224
const
  arr = [],
  traceMethods = ['push', 'unshift'],
  proxiedArr = new Proxy(arr, {
    get(target, prop, receiver) {
      if (traceMethods.includes(prop)) {
        return function(item) {
          console.log(`${prop}ing ${item}`);
          return target[prop](...arguments);
        }
      }
      return target[prop];
    },
  });

proxiedArr.push('e'); // logs 'pushing e' to the console
proxiedArr.unshift('f'); // logs 'unshifting f' to the console

console.log(arr); // ["f", "e"]
.as-console-wrapper { top: 0; max-height: 100% !important; }

2 Comments

Yeah, but the OP is wanting to intercept existing arrays,
@Keith noted, this is a little more cumbersome, but a bit safer. It does not override the default prototype method, although Konrad's shows that you can achieve this with the use of apply() and Reflect.
2

Capturing push method on all arrays on a webpage can easily be done by overriding the prototype method.

But, a big warning, doing this could potentially cause side effect. In fact a long time ago in Javascript land they used to be a lib called prototype that actually did this, and got a lot of flack for doing it.

Saying that, there is one area were overriding built-in is recommended, and that's pollyfilling new ES features.

If all your wanting to do is log into the console, maybe for debugging purposes, then your maybe OK.

So with all the warnings out the way, here is an example.

//keep a reference to the old push method
const oldPush = Array.prototype.push;
//now implement our replacement.
Array.prototype.push = function (...args) {
  console.log(...args);
  return oldPush.call(this, ...args);
}

//lets test.
const a = []
a.push(1)
a.push(1,2,3);
a.push('hello','there');

1 Comment

"a long time ago in Javascript land they used to be a lib called prototype that actually did this, and got a lot of flack for doing it." at the same ancient times, there was also a lib called MooTools that also did that. It caught some flak. Especially when it broke adding contains method to arrays by defining a contains itself. The method was then renamed to includes() (and the string one to match) which meant the library literally changed the web. Not necessarily for the better.
0

Of course, you could also do this without the Proxy for a solution that doesn't change how Arrays work for the rest of the code (which can produce the kind of issue that is hard to troubleshoot):

function push (arr, ...args) {
  console.log('Pushed called with', ...args)
  arr.push(...args)
}

const arr = []

push(arr, 1)
console.log('arr', arr)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.