如何使用代理来观察变量是否(不再)未定义

How to use Proxy to watch if a variable is (no longer) undefined

请原谅我的无能... 我正在尝试在全局 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 错误)。在这种情况下如何使用 Proxy?为什么我不能observe undefined stage with Proxy 直到它被实例化,然后执行回调?

仅供参考:

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

更新答案

您说过您无法控制 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.

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

let myObj;

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

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