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

34
我正在使用一个库将东西转换为ES6代理对象,另一个库因为我传递了其中一个(我的代码很糟糕,我知道)而出现了问题,我无法弄清楚如何取消代理对象的代理。但是我只是太蠢了。代理可以做任何事!

1
为什么库在代理上出现问题?请发布库和代理的(相关部分)代码,以便我们帮助您修复处理程序。 - Bergi
9个回答

38

我找到了一个技巧。在我的情况下,我无法控制代理的创建(mobx可观察值)。所以解决方案是:

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

2
聪明的 :) 有时候事情可以如此简单,谢谢! - Basti
1
这最终会丢失一些东西吗?人们似乎喜欢它。 - Sigfried
2
@Sigfried 嗯,正如接受的答案中所说,它会丢失一些内容,但我想当您的对象中没有这些属性时,这是最常见的情况。我认为它至少只能用作调试工具,因为我仅在此目的下使用它。JSON.stringify({ key: undefined }); JSON.stringify({ key: Symbol() }); JSON.stringify({ key: function(){} }); // 所有将转换为 "{}" - Alex Park
1
这根本不聪明 - 在解析和字符串化后,您的日期将会混乱。 - eXception
它比深度克隆慢得多。 - Marek Marczak
不要这样做,因为在此转换后你的日期会混乱。 - eXception

24

尝试使用JSON.parse(JSON.stringify(proxyObj))但这会删除任何无法字符串化的内容(例如类、函数、回调等...),这对我的用例不好,因为我在对象中有一些回调函数声明,并且我希望将它们视为对象的一部分。然而,我发现使用Lodash cloneDeep函数可以很好地将代理对象转换为POJO,并保持对象结构。

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

这对我很有吸引力,因为我通常信任lodash,并且它隐藏了所有令人困惑的东西。我不再使用那个让我犯错的库,所以我无法测试不同的答案。如果有人反对我选择这个作为官方答案,请告诉我,我会重新考虑。 - Sigfried

14

使用 展开运算符 怎么样?

 const plainObject = { ...proxyObject };

1
因为它适用于我的情况,所以我赞同了它,但似乎只能删除对象“根”上的代理。 - a2f0
点赞。这是最简单的方法,不使用任何库。 - shtse8
4
无法处理嵌套的 Proxy(它只是一个浅拷贝),要递归地复制/克隆整个对象,请使用 _.cloneDeep() - nezort11

5
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(...))


2
这将导致{a: undefined}而不是{a: 1},如果get处理程序仍然存在。否则,({...proxyObj})更短。 - Sebastian Simon
2
你的 get 处理程序需要一个 else 分支,在非 target 属性上正常运行(即使用 Reflect.get)。 - Bergi
如果你们中有人想要添加一个答案,我很乐意删除我搞砸的那个。 - Sigfried

4

如果您不想使用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对象,则此函数可能无法正常工作,但您可以按照相同的逻辑修改函数以解决该问题。

如果您想取消Vue3代理的代理状态,则Vue3提供了一个函数:toRaw


2
感谢您提供答案。您是否可以编辑您的答案并包含代码解释?这将有助于未来的读者更好地理解正在发生的事情,特别是那些对该语言感到陌生并且难以理解概念的社区成员。当已经有一个被社区验证的已接受答案时,这尤其重要。在什么条件下可能会优先考虑您的方法?您是否利用了新的功能? - Jeremy Caney
1
toRaw 函数不返回 POJO 对象。这是文档中的误解和错误。正如您在此处所看到的,它使用一个标志禁用了响应性,仅此而已。在使用 Web Workers 和 Vue 文档时,这种误解是 bug 的来源,因此应尽快修复 Vue 文档。https://github.com/vuejs/vue/blob/main/src/v3/reactivity/reactive.ts - Marek Marczak

2
假设您无法访问原始目标,则最快将 Proxy 转换为普通的 Object 的方法是将其值assign给一个新的对象:
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));


请解释您的解决方案。仅有代码而没有解释的答案会被标记为低质量。 - cursorrux
当我尝试了你的两个建议后,它只是创建了一个包含代理的新对象,这并没有解决问题。 - McGrew
两个示例在发布之前已经过测试,因此我添加了一个示例来显示每个新对象的输出。 - Neil Rackett

2
感谢 @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);

谢谢。我编辑了我的内容,让人们参考这个。如果有人总结并比较不同答案的优缺点,那就太好了。也许如果没有其他人这样做,我以后会这样做。 - Sigfried

2
通常情况下,你会使用mobx工具函数toJS()来实现此功能。
import { observable, toJS } from "mobx";

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

const obj = toJS(observed)

0
在未代理的对象构造函数中,添加this.self=this
然后确保您的获取处理程序允许返回self属性,这样就可以了。 proxiedMyObj.self===myObj //返回true

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接