如何获取JavaScript代理的目标?

Ada*_*dam 5 javascript proxy ecmascript-6

function createProxy() {
    const myArray = [Math.random(), Math.random()];
    return new Proxy(myArray, {});
}

const myProxy = createProxy();
Run Code Online (Sandbox Code Playgroud)

如何访问这里的target(是myArraymyProxy

我尝试了很多方法。在Google上搜索了许多博客文章,但没有找到目标的方法:(

Cal*_*ner 71

如果您使用 Vue 3 并处理代理,Vue 提供了一些方法来帮助解决此问题:

import { isProxy, toRaw } from 'vue';
Run Code Online (Sandbox Code Playgroud)

使用这些,您可以检查对象是否是代理isProxy,例如:

isProxy(reactiveObjectOrArray) ? 'yup' : 'nope'
Run Code Online (Sandbox Code Playgroud)

您可以使用以下方法提取原始数据toRaw

const rawObjectOrArray = toRaw(reactiveObjectOrArray)
Run Code Online (Sandbox Code Playgroud)

有关toRawisProxy的更多信息

  • 奇怪的是,“isProxy”和“toRaw”似乎只适用于使用“reactive”函数创建的响应式对象,而不适用于更常见的“ref”。你到底是如何获取原始对象的“ref”反应值的? (2认同)

Ras*_*leh 23

您可以使用以下命令复制代理返回的数据Object.assign()

const target_copy = Object.assign({}, my_proxy);
Run Code Online (Sandbox Code Playgroud)

这将适用于代理/目标上存在的所有可枚举自己的属性

  • 这并不能回答问题。`target_copy` 的数据仍然会受到代理访问器的影响;并且您将不会获得对原始对象的引用。 (5认同)
  • 如果目标是一个数组并且您想取回该数组,则此方法不起作用。 (5认同)

小智 6

有一种巧妙的方法-您可以将get陷阱添加到代理,并让它有条件地返回目标。像这样

let resolveMode = false;  // Switch that controls if getter returns target or prop. 

function resolve(obj) {
    resolveMode = true;  // Turn on our switch
    let target = obj.anything;  // This gets the target not the prop!
    resolveMode = false;  // Turn off the switch for the getter to behave normally
    return target;  // Return what we got!
}

function createProxy() {
    const myArray = [Math.random(), Math.random()];
    return new Proxy(myArray, {
        get: function(target, prop) {
            if (resolveMode) return target;  // This is where the magic happens!
            else return target[prop];        // This is normal behavior..
        }
    });
}

const myProxy = createProxy();
let target = resolve(myProxy);
Run Code Online (Sandbox Code Playgroud)

请记住,添加到陷阱的代码行越多,对象的性能就越慢。希望这可以帮助。

  • 仅当您有权访问代理的源代码时,这才是一个选项。 (2认同)

Dav*_*adi 6

就像已经说过的其他答案一样,代理获取陷阱可能是一个优雅的解决方案。

const IDENTITY = Symbol('proxy_target_identity')
const handler = {
  get: (target, property, receiver) => {
    if (property === IDENTITY) {
      return target
    }
    return Reflect.get(target, property, receiver)
  }
}
function createProxy() {
    const myArray = [Math.random(), Math.random()];
    return new Proxy(myArray, handler);
}
const myProxy = createProxy();
const orignal_target = myProxy[IDENTITY]
Run Code Online (Sandbox Code Playgroud)

此代码示例应该非常健壮,因为它:

  • 通过使用 a 避免属性名称冲突 Symbol
  • 通过使用Reflect.get而不是覆盖所有获取场景target[property]
  • 警告:小心原型继承——如果你的代理最终被用作原型,not_itself_a_proxy[IDENTITY]调用将不会返回,not_itself_a_proxy而是原型的“身份”!


tim*_*kay 5

其他答案给出了一些很好的解决方案。这是@Yuci 为类提炼出来的答案,在这种情况下,它就像定义一些特殊名称的实例变量一样简单。Proxy get 函数返回它,底层目标也是如此。

class Foo {
    constructor() {
        this.__target__ = this;
        return new Proxy(this, {
            get: function (target, name) {
                if (name in target) return target[name];
                // your code here
            }
        });
    }
}

let foo = new Foo();
let target = foo.__target__;
console.log('proxied Foo', foo);
console.log('recovered target', target, target.__target__.__target__);
Run Code Online (Sandbox Code Playgroud)


sgr*_*yon 5

我发现(使用 Vue.js 有时会涉及代理对象,例如在观看组件道具时)如果它是一个对象,如果它是一个数组,我可以使用以下方法获取目标JSON.stringify

let myTarget = JSON.parse(JSON.stringify(myProxy))
Run Code Online (Sandbox Code Playgroud)

这种方法也适用于数组目标,而Object.assign({}, myProxy)仅适用于目标是对象。

但是我对 JavaScript 代理很陌生,而且我的知识有限。我可能不理解这种方法的局限性和注意事项。尽管如此,也许它可以帮助某人!

  • 谢谢。这就是我所需要的。我需要 Vue 创建的代理的目标,因此我可以将其作为获取请求的一部分传递。 (5认同)
  • 对于旧版本来说是个好答案,但如果在 Vue3 上,请考虑 [toRaw](/sf/answers/4956362211/)... (2认同)