我是 ES6 Proxy
对象的新手,在尝试对已代理的数组调用 concat
时遇到了一个我不明白的错误。
背景:
我认为 ES6 Proxy
可以完美地作为一种方式来验证我的 React/Redux 应用程序中 reducer 函数的“纯度”。我可以将我的状态对象包装在代理中,如果我尝试改变该对象,该代理会抛出错误。我正在使用基于 on-change 的东西库来执行此操作:
const triggersOnChange = (object, onChange) => {
const handler = {
get (target, property, receiver) {
try {
return new Proxy(target[property], handler)
} catch (err) {
return Reflect.get(target, property, receiver);
}
}
defineProperty (target, property, descriptor) {
onChange()
return Reflect.defineProperty(target, property, descriptor)
}
deleteProperty (target, property) {
onChange()
return Reflect.deleteProperty(target, property)
}
}
return new Proxy(object, handler)
}
这是我打算如何使用代理包装器的示例测试:
describe('reducer', () => {
test('it returns an updated state object', () => {
const state = triggersOnChange({ items: [] }, () => {
throw new Error('Oops! You mutated the state object')
})
const action = {
payload: { item: 'foobar' }
}
expect(reducer(state, action)).toEqual({
items: [action.payload.item]
})
})
})
如果我实现了一个会改变状态对象的“坏”reducer,我的测试会按预期抛出错误:
const reducer = (state, action) => {
state.items.push(action.payload.item) // bad
return state
}
// test throws error "Oops! You mutated the state object"
但是当我通过返回一个新的状态对象来“净化”我的 reducer 时,我得到了一个我不太理解的不同错误:
const reducer = (state, action) => {
return Object.assign({}, state, {
items: state.items.concat(action.payload.item)
})
}
/*
TypeError: 'get' on proxy: property 'prototype' is a read-only and
non-configurable data property on the proxy target but the proxy did
not return its actual value (expected '[object Array]' but got
'[object Object]')
at Proxy.concat (<anonymous>)
*/
我在这里遗漏了一些有关代理行为的信息吗?或者这可能是我的 get
陷阱导致的代理链接行为的问题?我最初认为这是在 Object.assign
中使用代理的问题,但是在我的 reducer 返回语句之前调试时遇到了同样的错误,我实际上在其中使用了 Object.assign
>。救命!
编辑:很高兴修改这个问题,使其更加通用,但我不是 100% 知道问题是什么,所以我会等待,看看是否能得到任何答案。
最佳答案
可以使用以下代码复制您的问题:
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;
问题的核心是对象具有必须保持一致的不变量,即使是通过代理对象访问时也是如此,在这种情况下,您将破坏这些不变量之一。如果你看the specification for Proxy's get
,它指出:
[[Get]] for proxy objects enforces the following invariants:
- The value reported for a property must be the same as the value of the corresponding target object property if the target object property is a non-writable, non-configurable own data property.
- The value reported for a property must be undefined if the corresponding target object property is a non-configurable own accessor property that has undefined as its [[Get]] attribute.
在您的情况下,您不会维护第一个不变式,因为即使属性不可写且不可配置,您也会返回一个包装代理。最简单的方法是确保在这种情况下返回正确的值。
当我们这样做时,我还建议显式使用 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;
},
关于javascript - 在代理数组上调用 concat 会引发错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48494674/