【问题标题】:Equality between Proxy and target object代理和目标对象之间的平等
【发布时间】:2016-03-27 16:41:56
【问题描述】:

Javascript 的新代理功能为调试提供了一些有趣的功能。例如,您可以通过将对象放在带有get 处理程序的代理后面来“保护”一个对象,如果您访问未定义的属性,该处理程序将抛出。这有助于发现拼写错误和其他类型的错误。

可以这样使用:

class Something {
    constructor()
    {
        this.foo = "bar";

        allObjects.push(this);
    }
};

function defend(o)
{
    return new Proxy(o, DebugCheckHandler);
};

let rawSomething = new Something();
let defendedSomething = defend(rawSomething);

可以认真编写代码以仅处理defendedSomething。然而在这个例子中,Something 构造函数将this 传递到其他地方(到allObjects)。这最终与在代码库中同时使用 rawSomethingdefendedSomething 具有相同的效果。

问题源于代理引用不等于其原始引用,因为rawSomething !== defendedSomething。例如,如果allObjects.includes(defendedSomething) 包含rawSomething,它将返回false,因为includes 会执行严格的=== 检查。

有没有什么好的方法可以不用对代码做太多改动来解决这个问题?

【问题讨论】:

  • 在构造函数中使用全局变量(或任何外部变量)是非常糟糕的工程,allObjects.push(this); 应该在外部,例如allObjects.push(rawSomething);

标签: javascript ecmascript-6 proxies


【解决方案1】:

Iirc,在构造函数中影响this 值的唯一方法——这是你在这里需要的——是通过子类化。我相信(但无法测试)以下内容应该有效:

function Defended() {
    return new Proxy(this, DebugCheckHandler);
//                   ^^^^ will become the subclass instance
}

class Something extends Defended {
//              ^^^^^^^^^^^^^^^^ these…
    constructor() {
        super();
//      ^^^^^^^ …should be the only changes necessary
//      and `this` is now a Proxy exotic object
        this.foo = "bar";

        allObjects.push(this);
    }
};

let defendedSomething = new Something();

【讨论】:

  • 我认为问题在于并非所有Something 实例都应该受到保护。
  • 嗯,在这种情况下,您需要将一个布尔参数从Something 构造函数向上传递给Defended,并使其类似于function Defendable(defend) { if (defend) return new Proxy …; }
  • 不,真正的问题是如果 DebugCheckHandler 在添加不存在的属性时抛出(我想要它),那么在添加“foo”属性时,Something 类构造函数将抛出。
  • 嗯,听起来您确实需要调整构造函数代码并明确决定要在哪里使用代理(用于传递)和目标(用于初始化)。当然,您也可以包装构造函数以创建一个(临时)区域,在此期间允许添加属性,但这也可能适得其反。
【解决方案2】:

您可以:

  1. 创建一个继承自构造函数的prototype 的对象。
  2. 将其用作代理的处理程序。
  3. 调用将代理作为this 值传递的构造函数。
function Something() {
  this.foo = "bar";
  allObjects.push(this);
}
function defendIntanceOf(C) {
  var p = new Proxy(Object.create(C.prototype), DebugCheckHandler);
  C.call(p);
  return p;
};
let defendedSomething = defendIntanceOf(Something);

请注意,我使用函数语法而不是类一,以便能够使用 Function.call 使用自定义 this 值调用 [[Call]]。

【讨论】:

  • 一个问题是如果DebugCheckHandler在设置不存在的属性时抛出,它会在构造函数添加对象属性时抛出。
  • @AshleysBrain 然后让它不扔?或者var p = new Proxy(new C(), DebugCheckHandler),如果您不介意将无用的对象推送到allObjects。或者var p = new Proxy(Object.assign(Object.create(C.prototype), {foo: void 0}), DebugCheckHandler),如果你现在事先知道属性。
  • 我正在寻找一种解决方案,它可以避免调试检查处理程序在构造函数中发现误报,永远不会将无用的对象推送到 allObjects,并且不需要完全重写构造函数或以其他方式使用相当不同的样式.
  • @AshleysBrain 您是否可以在构建对象后更改陷阱? Example
  • 这不适用于classes,它只能被 [[construct]]ed 而不是 [[call]]ed。而Reflect.construct 确实将(子类)构造函数作为其第三个参数,而不是实例。
猜你喜欢
  • 1970-01-01
  • 2014-09-07
  • 2017-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-20
  • 1970-01-01
相关资源
最近更新 更多