JavaScript 代理目标对象与 属性 描述符冲突

JavaScript Proxy target object conflicts with property descriptors

我正在创建一个虚拟化对象,它使用规则引擎延迟构建其属性,作为一种跳过计算从未读取的值的技术。为此,我使用了 Proxy。代理似乎在访问转发和虚拟化方面起到了双重作用;就我而言,我主要关心后者,而不是前者。

我遇到的问题与尝试在代理中实施 getOwnPropertyDescriptor 陷阱有关。这样做时出现错误:

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

因为我实际上并没有将请求转发到包装对象,所以我一直在使用 Object.freeze({}) 作为传递给 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');

代理对象类似于具有只读属性的对象,不能扩展或具有任何其他类似的幻想。我尝试使用冻结且不可扩展的对象,但我仍然被告知 属性 描述符不兼容。

当我尝试使用非冻结对象作为代理目标时,出现不同类型的错误:

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

我在这里做错了什么?有什么方法可以让我的代理展示不可配置性?

您可能想查阅 invariantshandler.getOwnPropertyDescriptor() 列表,不幸的是,其中包括:

  • 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.

由于您的 target 是一个冻结的空对象,因此您的 handler.getOwnPropertyDescriptor() 陷阱的唯一有效 return 值是 undefined


不过,要解决根本问题,请在尝试访问时延迟初始化 属性 描述符:

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'));

您可以针对每个方法陷阱遵循此模式,通过延迟初始化访问的任何属性来抢占对您的 dummyObj 不需要的突变。