简体   繁体   中英

JavaScript Proxy target object conflicts with property descriptors

I'm creating a virtualized object that lazily builds its properties with a rules engine as a technique to skip calculating values that are never read. For this, I'm using a Proxy . It seems like proxies sort of serve double-duty for both access forwarding and virtualizing; in my case, I mostly care about the latter, not the former.

The problem I'm having has to do with trying to implement the getOwnPropertyDescriptor trap in the proxy. When doing so I get the error:

TypeError: 'getOwnPropertyDescriptor' on proxy: trap returned descriptor for property 'foo' that is incompatible with the existing property in the proxy target

Because I'm not actually forwarding requests to a wrapped object, I've been using an Object.freeze({}) as the first argument passed to new Proxy(...) .

function createLazyRuleEvaluator(rulesEngine, settings) {
  const dummyObj = Object.freeze({});
  Object.preventExtensions(dummyObj);

  const valueCache = new Map();

  const handlers = {
    get(baseValue, prop, target) {
      if (valueCache.has(prop)) 
        return valueCache.get(prop);

      const value = rulesEngine.resolveValue(settings, prop);
      valueCache.set(prop, value);
      return value;
    },

    getOwnPropertyDescriptor(baseValue, prop) {
      return !propIsInSettings(prop, settings) ? undefined : {
        configurable: false,
        enumerable: true,
        get() {
          return handlers.get(baseValue, prop, null);
        }
      };
    },
  };

  return new Proxy(dummyObj, handlers);
}

// This throws
Object.getOwnPropertyDescriptor(createLazyRuleEvaluator(/*...*/), 'foo');

The proxy object is meant to resemble a object with read-only properties that can't be extended or have any other such fanciness. I tried using a frozen and non-extensible object, but I'm still told the property descriptor is incompatible.

When I try using a non-frozen object as the proxy target, then I get a different type error:

TypeError: 'getOwnPropertyDescriptor' on proxy: trap reported non-configurability for property 'foo' which is either non-existent or configurable in the proxy target

What am I doing wrong here? Is there any way I can have my proxy exhibit non-configurability?

You might want to consult the list of invariants for handler.getOwnPropertyDescriptor() , which unfortunately includes these:

  • A property cannot be reported as existent, if it does not exist as an own property of the target object and the target object is not extensible.
  • A property cannot be reported as non-configurable, if it does not exist as an own property of the target object or if it exists as a configurable own property of the target object.

Since your target is a frozen, empty object, the only valid return value for your handler.getOwnPropertyDescriptor() trap is undefined .


To address the underlying question though, lazily initialize your property descriptors when they're attempted to be accessed:

 function createLazyRuleEvaluator(rulesEngine, settings) { const dummyObj = Object.create(null); const valueCache = new Map(); const handlers = { get(target, prop) { if (valueCache.has(prop)) return valueCache.get(prop); const value = prop + '_value'; // rulesEngine.resolveValue(settings, prop) valueCache.set(prop, value); return value; }, getOwnPropertyDescriptor(target, prop) { const descriptor = Reflect.getOwnPropertyDescriptor(target, prop) || { value: handlers.get(target, prop) }; Object.defineProperty(target, prop, descriptor); return descriptor; }, }; return new Proxy(dummyObj, handlers); } console.log(Object.getOwnPropertyDescriptor(createLazyRuleEvaluator(/*...*/), 'foo'));

You can follow this pattern for each of the method traps to preempt unwanted mutations to your dummyObj by lazily initializing any properties accessed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM