【问题标题】:How to use Proxy to watch if a variable is (no longer) undefined如何使用代理来观察变量是否(不再)未定义
【发布时间】:2018-04-17 10:47:10
【问题描述】:

请原谅我的无能... 在全局 var 阶段不再未定义后,我正在尝试使用 Proxy 执行一些代码。这是我天真的尝试:

```

var stage = undefined
let myObj;
let st = {
    stage: stage,
}
let lr = {
    get: function(obj:any, prop:any):LightRay{
        myObj = new SomeObjThatNeedsStage(obj[prop]) // <= supposed to be non-undefined stage
        return myObj
    },
}
let p = new Proxy(st, lr)
// At this point I expect the lr handler to have been executed if the stage var has been instantiated by some external code that's out of my control.

```

显然,我误解了代理的工作原理,但我尝试了各种方案,但都不起作用。将未定义阶段作为目标传递给代理不起作用(出现Cannot create proxy with a non-object as target or handler 错误)。在这种情况下如何使用代理?为什么我不能用Proxyobserve undefined stage 直到实例化,然后再执行回调?

仅供参考:

Stage var 是全局变量,我无法控制它(它有时由 create.js 实例化)。所有代码都从 UMD 模块执行。我正在使用打字稿(转译为 es5)。 Object.defineProperty() 在我的浏览器中可用。此外,stage(createjs.Stage 类型)在实例化时确实包含不同类型的 setter/getter,这在使用 typescript 时会出现问题。

【问题讨论】:

  • 这听起来像是X/Y problem。您为什么认为要编写代码来监视全局变量的值是否发生变化?
  • 应该定义什么stage变量?
  • 仅供参考:观察变化是反模式和代码维护的噩梦。尽可能寻找任何其他解决方案,例如使一个模块依赖于另一个模块,以便在模块运行之前知道它已加载。
  • 是的,我知道这是个坏主意,但我不得不处理它,因为 Adob​​e Animate CC 导出 Create.js 代码的方式很奇怪,而且 Create.js 烦人地没有发出 ('ready ') 事件会告诉我舞台已启动并正在运行。它不在我的控制范围内。
  • "在这种情况下如何使用代理?" - 你不能。

标签: javascript ecmascript-6 es6-proxy


【解决方案1】:

更新答案

你说过你无法控制stage 的设置方式。

观察变化是反模式和代码维护的噩梦。尽可能寻找任何其他解决方案,例如使一个模块依赖于另一个模块,以便在模块运行之前知道它已加载。

但是,你说过没有别的办法,所以:

您不能为此使用代理,因为要使其生效,代码设置stage 必须通过代理进行设置,而不是直接设置。

您说过您无法控制 stage 的声明方式。这意味着您也不能为此使用Object.defineProperty,因为var 创建的全局对象上的属性具有configurable: false

如果绝对没有办法在您的模块和正在做的事情stage 之间进行协调,您将不得不求助于计时器循环。这不是一个好主意,但可能是您唯一真正的选择:

var stop = Date.now() + 30000;
var timer = setInterval(function() {
    if (typeof stage !== "undefined" || Date.now() > stop) {
        clearInterval(timer);
        timer = 0;
        // Your code here, if `stage` is still undefined, you hit the timeout
    }
}, 50); // 50ms interval or whatever you need

原答案

到目前为止最好的解决方案是:不要那样做。相反,使用带有 setter 的对象:

var stuff = {
    _stage: undefined,
    get stage() {
        return _stage;
    },
    set stage(value) {
        if (_stage === undefined && value !== undefined) {
            _stage = value;
            // Trigger your code here
        }
    }
};

然后让其他代码执行此设置stuff.stage,而不是设置stage

【讨论】:

  • 我们可以将Object.defineProperty用于这种目的吗?
  • @zabusa:是的,但不能保证与主机提供的对象一起使用。 (也就是说:它可能适用于window/全局对象。)
  • 是的,window/document 和 getter 从来没有问题。
  • @dfsq:很好很好。在过去的几年里,浏览器在使它们的主机提供的对象与 JS 兼容方面做得非常好。
  • 啊,抱歉,我的回答太仓促了。没有足够注意您的更新版本。我很抱歉。只是仔细看看你的答案。本质上是一样的,你还有一个额外的好处,那就是让计时器超时,因为它需要太长时间,所以我撤销了我的答案。你的肯定更好。干杯。
【解决方案2】:

如果你真的需要观察 stage 被其他东西定义时的变化,并且 stage 是全局的,那么简单的解决方案是使用旧的好 getter:

let myObj;

Object.defineProperty(window, 'stage1', {
  set (value) {
    myObj = new SomeObjThatNeedsStage(stage)
    return value
  }
})

在上面,确保你没有var stage = undefined,你不再需要它了。

【讨论】:

  • ...provided defineProperty 作用于全局对象,在浏览器上是主机提供的;主机提供的对象不一定要支持这一点。另外:set 不应该返回值,你可能需要一个get
  • 不起作用。 setter 永远不会被调用。顺便说一句,是的,舞台是全球性的,我无法控制它。您提供的代码是从 UMD 模块执行的。我正在使用打字稿(转译为 es5)。 Object.defineProperty() 在我的浏览器中可用。
猜你喜欢
  • 2013-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多