如何将 ES6 代理转回普通对象 (POJO)?

how do I turn an ES6 Proxy back into a plain object (POJO)?

我正在使用一个将事物转换为 ES6 代理对象的库,以及另一个我认为令人窒息的库,因为我将其中一个传递给它(我知道我的代码是一个讽刺),我不能不知道如何取消代理代理对象。

但我只是在装傻。代理无所不能!

pp = new Proxy(
   {a:1},
   {
      get: function(target, prop, receiver) { 
             if(prop==='target') return target 
           }
   }
)

但这只有在您可以控制代理的创建时才有效。事实证明它更容易:

pojo = Object.assign({}, proxyObj) // won't unwrap nested proxies though

对于可能喜欢这个答案的读者,David Gilbertson 的新答案可能会更好。我个人更喜欢lodash clonedeep。最受欢迎的似乎是 JSON.parse(JSON.stringify(...))

我发现了一个技巧。就我而言,我无法控制代理的创建(mobx 可观察值)。所以解决方案是:

JSON.parse(JSON.stringify(your.object))

在未代理的对象构造函数中,添加this.self=this

然后确保您的 get 处理程序允许返回 self 属性 并且一切就绪。 proxiedMyObj.self===myObj //returns true

尝试使用 JSON.parse(JSON.stringify(proxyObj)),但这会删除任何无法字符串化的内容(如 类、函数、回调等...),这对我的用例不利,因为我有一些我的对象中的回调函数声明,我希望将它们视为我的对象的一部分。但是我发现使用 Lodash cloneDeep 函数在将 Proxy 对象转换为 POJO 的同时保持对象结构方面确实做得很好。

convertProxyObjectToPojo(proxyObj) {
  return _.cloneDeep(proxyObj);
}

谢谢@sigfried 的回答,这正是我要找的!

这里有一个更详细的版本,使用 Symbol 以避免与真实道具名称冲突。

const original = {foo: 'bar'};

const handler = {
  get(target, prop) {
    if (prop === Symbol.for('ORIGINAL')) return target;

    return Reflect.get(target, prop);
  }
};

const proxied = new Proxy(original, handler);

console.assert(original !== proxied);

const unproxied = proxied[Symbol.for('ORIGINAL')];

console.assert(original === unproxied);

通常你会为此使用 mobx util toJS()。

import { observable, toJS } from "mobx";

const observed = observable({ foo: 'bar' })

const obj = toJS(observed)

使用 Spread Operator 怎么样?

 const plainObject = { ...proxyObject };

假设您无法访问原始目标,将 Proxy 转换为 assign 其值的最快方法是将其值 Object 转换为新的普通 Object:

const obj = Object.assign({}, proxy);

或使用吊具:

const obj = { ...proxy };

例子

const user = {
  firstName: 'John',
  lastName: 'Doe',
  email: 'john.doe@example.com',
};

const handler = {
  get(target, property) {
    return target[property];
  }
};

const proxy = new Proxy(user, handler);

const a = Object.assign({}, proxy);
console.log(`Using Object.assign():`);
console.log(`Hello, ${a.firstName} ${a.lastName}!`);
console.log(JSON.stringify(a));

const s = { ...proxy };
console.log(`Using spreader:`);
console.log(`Hello, ${s.firstName} ${s.lastName}!`);
console.log(JSON.stringify(s));

如果你不想使用lodash object.assign({},val)方法就可以了。但是如果 val 包含嵌套对象,它们将被代理。所以它必须像这样递归地完成:

function unproxify(val) {
    if (val instanceof Array) return val.map(unproxify)
    if (val instanceof Object) return Object.fromEntries(Object.entries(Object.assign({},val)).map(([k,v])=>[k,unproxify(v)]))
    return val
}

其实就是深度克隆功能。我认为如果 val 包含 Map 对象,它不起作用,但您可以按照相同的逻辑修改函数。

如果你想unproxify一个Vue3代理,Vue3提供了一个函数:toRaw