在代理数组上调用 concat 会引发错误

djf*_*dev 1 javascript ecmascript-6 redux

我是 ES6 对象的新手,在尝试调用已代理的数组Proxy时遇到了我不明白的错误。concat

\n\n

背景:

\n\n

我认为 ES6Proxy可以完美地作为一种方式来验证我的 React/Redux 应用程序中的减速器函数的“纯度”。我可以将我的状态对象包装在代理中,如果我尝试改变该对象,该代理会抛出错误。我正在使用基于on-change库的东西来执行此操作:

\n\n
const triggersOnChange = (object, onChange) => {\n  const handler = {\n    get (target, property, receiver) {\n      try {\n        return new Proxy(target[property], handler)\n      } catch (err) {\n        return Reflect.get(target, property, receiver);\n      }\n    }\n\n    defineProperty (target, property, descriptor) {\n      onChange()\n      return Reflect.defineProperty(target, property, descriptor)\n    }\n\n    deleteProperty (target, property) {\n      onChange()\n      return Reflect.deleteProperty(target, property)\n    }\n  }\n\n  return new Proxy(object, handler)\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是我打算如何使用代理包装器的示例测试:

\n\n
describe(\'reducer\', () => {\n  test(\'it returns an updated state object\', () => {\n    const state = triggersOnChange({ items: [] }, () => {\n      throw new Error(\'Oops! You mutated the state object\')\n    })\n\n    const action = {\n      payload: { item: \'foobar\' }\n    }\n\n    expect(reducer(state, action)).toEqual({\n      items: [action.payload.item]\n    })\n  })\n})\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果我实现了一个改变状态对象的“坏”减速器,我的测试会按预期抛出错误:

\n\n
const reducer = (state, action) => {\n  state.items.push(action.payload.item) // bad\n  return state\n}\n\n// test throws error "Oops! You mutated the state object"\n
Run Code Online (Sandbox Code Playgroud)\n\n

但是当我通过返回一个新的状态对象来“净化”我的减速器时,我得到了一个我不太理解的不同错误:

\n\n
const reducer = (state, action) => {\n  return Object.assign({}, state, {\n    items: state.items.concat(action.payload.item)\n  })\n}\n\n/* \n  TypeError: \'get\' on proxy: property \'prototype\' is a read-only and\n  non-configurable data property on the proxy target but the proxy did\n  not return its actual value (expected \'[object Array]\' but got\n  \'[object Object]\')\n      at Proxy.concat (<anonymous>)\n*/\n
Run Code Online (Sandbox Code Playgroud)\n\n

我在这里遗漏了一些有关代理行为的信息吗?或者这可能是我的陷阱导致的代理链接行为的问题get?我最初认为这是在 中使用代理的问题Object.assign,但是在我实际使用的减速器的 return 语句之前进行调试时,我遇到了同样的错误Object.assign。帮助!

\n\n

编辑:很高兴修改这个问题以使其更通用,但我\xe2\x80\x99m 不是 100% 问题是什么,所以我\xe2\x80\x99ll 等待,看看是否能得到任何答案。

\n

log*_*yth 5

可以使用以下代码复制您的问题:

var obj = {};
Object.defineProperty(obj, "prop", { 
  configurable: false,
  value: {},
});

var p = new Proxy(obj, {
  get(target, property, receiver) {
    return new Proxy(Reflect.get(target, property, receiver), {});
  },
});

var val = p.prop;
Run Code Online (Sandbox Code Playgroud)

问题的核心是对象具有必须保持一致的不变量,即使是通过代理对象访问时也是如此,在这种情况下,您将破坏这些不变量之一。如果您查看Proxy's 的规范get,它会指出:

代理对象的 [[Get]] 强制执行以下不变量:

  • 如果目标对象属性是不可写、不可配置的自有数据属性,则为属性报告的值必须与相应目标对象属性的值相同。
  • 如果相应的目标对象属性是其 [[Get]] 属性未定义的不可配置的自身访问器属性,则为属性报告的值必须为未定义。

在您的情况下,您不会维护第一个不变式,因为即使属性不可写且不可配置,您也会返回一个包装代理。最简单的方法是确保在这种情况下返回正确的值。

当我们这样做时,我还建议typeof显式使用而不是使用try/ catch,这样会更清晰。

get(target, property, receiver) {
  const desc = Object.getOwnPropertyDescriptor(target, property);
  const value = Reflect.get(target, property, receiver);

  if (desc && !desc.writable && !desc.configurable) return value;

  if (typeof value === "object" && value !== null) return new Proxy(value, handler);
  else return value;
},
Run Code Online (Sandbox Code Playgroud)