1

enter image description here

const globalProxy = new Proxy(win.window, {
  get(target, p, receiver){
    // return undefined
    return Reflect.get(target, p, receiver);
    // there is a value
    // return Reflect.get(target, p);
  }
})

In Node.js version 20.18, when using Reflect.get with the receiver parameter, it returns undefined, while without the receiver parameter, there is a value. Based on my limited understanding of the Reflect.get function (which only affects the this pointer of getters), I really can't figure out why this is the case. I even asked Claude, but it didn't provide me with a reproducible example.

On Node.js version 16.20, there is a value. So, could it be a bug in V8?

code of image comes from vitest lib

when debugging in VSCode, when monitoring the target and expanding the list of member variables, there is no property named p (including expanding the [[Prototype]] prototype layer by layer). However, it exists on __proto__, and hasOwnProperty returns true, which also indicates that it shouldn't be on the prototype.

I can reproduce with this:

const myObj = {
    aa: 'aa'
}

Object.defineProperty(myObj, 'a', {
    get () {
        return this.aa
    }
})

const myProxyObj = new Proxy({
    a: 'a'
}, {
    get(target, prop, receiver) {
        return Reflect.get(target, prop, receiver)
    }
})

console.log(Reflect.get(myObj, 'a', myProxyObj))
console.log('a' in myObj)
console.log('a' in myProxyObj)
console.log(Object.hasOwn(myObj, 'a'))
console.log(Object.hasOwn(myProxyObj, 'a'))

but, in vscode debuger control panel, I get this:

// target === win.window
Object.entries(target).length // 183
Object.keys(target).length // 186

addEventListener aka p is not exists in entries function returns, besides, removeEventListener and dispatchEvent don't exist either

there is a reproduce demo may help: https://codesandbox.io/p/devbox/kc2h2l

steps:

  • add breakpoint in line 4 of dom.test.js file
  • npm run test in js debug terminal
  • watch window in debug panel, will get a proxy object
  • go to definition of proxy [[Handler]]'s get function then add a breakpoint(source map incorrect maybe)
  • if previous step not work, try to step in (Alt+F11) 2 - 4 times, will reach to line 317 of /node_modules/vitest/dist/vendor-entry.5c1e6d94.js file
  • now, you can watch target and receiver and reproduce above demo

1 Answer 1

0

In

get(target, p, receiver){
    // ...
}

target is the window/globalThis object, p is a string containing the name of the property being accessed, and receiver is the Proxy object.

Edited:

Reflect.get(target, propertyKey, receiver) is semantically equivalent to target[propertyKey]; if the property is a data property (a regular property). However, if the property is an accessor property (a getter/setter) then the getter is bound to the receiver before being called.

I believe this function I have written emulates what Reflect.get does. In all 3 of the test cases it returns the same result.

function whatReflectGetDoes(target, propertyKey, receiver) {
    const getter = Object.getOwnPropertyDescriptor(target, propertyKey).get;
    if (getter === undefined) {
        return target[propertyKey];
    } else {
        return getter.bind(receiver)();
    }
}

var obj = {
    foo: 42,    // data property
    get bar() { // accessor property
      return 21;
    },
};

// REAL 42 21
console.log("REAL", obj.foo, obj.bar);
// FAKE 42 21
console.log("FAKE", whatReflectGetDoes(obj, 'foo'), whatReflectGetDoes(obj, 'bar'));

// -----------------------

var obj = {
    bar: 42,
    get foo() {
       return this.bar;
    }
};

// REAL 21 42
console.log("REAL", Reflect.get(obj, 'foo', {bar: 21}), Reflect.get(obj, 'bar', {bar: 21}));
// FAKE 21 42
console.log("FAKE", whatReflectGetDoes(obj, 'foo', {bar: 21}), whatReflectGetDoes(obj, 'bar', {bar: 21}));

// -----------------------

var obj = {
    aa: "aa",
};

Object.defineProperty(obj, "a", {
    get() {
        return this.aa;
    },
});

const myProxyObj = new Proxy(
    { a: "a" },
    {
        get(target, prop, receiver) {
            return Reflect.get(target, prop, receiver);
        },
    }
);
// REAL undefined
console.log("REAL", Reflect.get(myObj, "a", myProxyObj));
// FAKE undefined
console.log("FAKE", whatReflectGetDoes(myObj, "a", myProxyObj));

There is not a bug in V8 because if you look at the ECMAScript specification here https://tc39.es/ecma262/multipage/reflection.html#sec-reflect.get it indicates that what my whatReflectGetDoes function does is the intended behavior. Rather I believe Mozilla's Reflect.get documentation (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/get) is incomplete.

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

3 Comments

there is a reproduce demo, thx for your help: codesandbox.io/p/devbox/kc2h2l
ok I see. I looked into it more and updated my answer because it would have been too long to post in a comment.
Your reimplementation ignores the prototype chain.

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.