1

I am working with JavaScript Proxy and do not understand why the target and the receiver are different in both the get() and set() traps. They appear to be the same (based on console logging). The mozilla docs say that the receiver is either the proxy or the object that inherits from the proxy; in this case, it's clearly not the proxy. (FWIW, this is the proxy itself in the get() and set() traps.)

This code:

const s = 'this is a string';
const o = {a: s};

const p = new Proxy(o, {
  get(target, prop, recv) {
    console.log('get: target', target, 'recv', recv, 'same', target === recv);
    return target[prop];
  },
  set(target, prop, value, recv) {
    console.log('set: target', target, 'recv', recv);
    Reflect.set(target, prop, value, recv);
    //target[prop] = value;
    console.log('set: target', target, 'recv', recv, 'same', target === recv);
    return value;
  },
});

let z = p.a;
p.a = 'also a string';
z = p.a;

results in this output:

get: target { a: 'this is a string' } recv { a: 'this is a string' } same false
set: target { a: 'this is a string' } recv { a: 'this is a string' }
set: target { a: 'also a string' } recv { a: 'also a string' } same false
get: target { a: 'also a string' } recv { a: 'also a string' } same false
bmacnaughton
  • 4,950
  • 3
  • 27
  • 36
  • 1
    It is something like `const a1 = { key: 0 }; const a2 = { key: 0 }`. Even though both `a1` and `a2` are `{ key: 0 }`, `a1 !== a2`. – Titus May 26 '23 at 17:27
  • i understand that two objects are not the same just because they have the same properties and values. but how would the receiver come into being? i don't think a proxy would clone an object just to have a receiver that looks like the original object. – bmacnaughton May 26 '23 at 17:33
  • Are you sure you get the result you've mentioned ? I've just run your code and I get an infinite recursion. – Titus May 26 '23 at 17:38
  • i am positive. i copied the code directly from the file and the output from the console. what version of node/os are you running? i am running v16.20.0 and ubuntu 22.04.2. (and i just ran it again to verify. the only difference is that i left out 'use strict'; on the first line.) – bmacnaughton May 26 '23 at 17:48
  • I've run it in the browser, I don't think NodeJS has a default implementation for `Proxy`. Are you using a module ? – Titus May 26 '23 at 17:56
  • [Actually, it does](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#browser_compatibility) but the implementation is definitely different from that used in browsers. In browsers, this code creates an infinite recursion. – Titus May 26 '23 at 18:03
  • i am running only in NodeJs; i have no need to run it in the browsers; sorry - i didn't even think of that. i should have been more clear. – bmacnaughton May 26 '23 at 18:44
  • 1
    @Titus - that's curious - Proxy is a v8 implementation; i would expect it to work the same in node and the browser (chrome, edge, brave). Do you see what would cause recursion in my code? I don't. I just ran it in the chrome debugging console and it ran fine. are you running firefox? I'm getting more curious as to what is going on. – bmacnaughton May 26 '23 at 19:15
  • "*They appear to be the same (based on console logging)*" - are you looking at the output in a terminal, or do you look at (and inspect) the console output in a debugger? – Bergi May 27 '23 at 13:33

1 Answers1

0

The reason is that the receiver is the proxied object and console.log() transparently renders it.

Adding this line console.log('this', this, 'p === recv', p === recv, this === recv); to the set() trap results in this output:

this { get: [Function: get], set: [Function: set] } p === recv true false

Mozilla docs say that this is bound to the handler, but it looks like this is bound to the object containing the handlers. And that would explain why this is rendered the way it is.

This could also explain why Titus saw infinite recursion in a browser implementation - console.log() is fetching the original object through the proxy to display recv. But I still can't explain why the node.js implementation does not recurse until the stack overflows.

bmacnaughton
  • 4,950
  • 3
  • 27
  • 36
  • 1
    `this` is the object of handlers (the second argument of the `Proxy` constructor), on which `.get()` is called as a method. – Bergi May 27 '23 at 13:34
  • 1
    `this === recv` and `p === recv` are two different things – Bergi May 27 '23 at 13:34
  • thanks for the comments @Bergi; i was in the process of updating my answer as i noticed that my answer was not thorough (and i'd left a bad label in the output). – bmacnaughton May 27 '23 at 13:38