如何使用 JavaScript 中的 ES6 代理检测任意私有成员的更改?

问题描述 投票:0回答:2

我正在努力使用 Javascript canvas API 构建 2D 渲染器,并且当任何可渲染对象的状态没有发生任何更改时,我尝试通过跳过渲染来提高性能。

为此,我想构建一个可扩展类(称之为

Watchable
),它将检测其状态的更改,同时允许子类对该状态跟踪保持不可知。客户端代码将扩展此类以创建可渲染对象,然后向渲染器注册这些对象。

经过一些工作,我使用代理得出了部分解决方案,并实现了

makeWatchable
功能,如下所示:

function makeWatchable(obj) {

    // Local dirty flag in the proxy's closure, but protected from
    // external access
    let dirty = true;

    return new Proxy(obj, {

        // I need to define accessors and mutators for the dirty
        // flag that can be called on the proxy.
        get(target, property) {

            // Define the markDirty() method
            if(property === "markDirty") {
                return () => {
                    dirty = true;
                };
            }

            // Define the markClean() method
            if(property === "markClean") {
                return () => {
                    dirty = false;
                };
            }

            // Define the isDirty() method
            if(property === "isDirty") {
                return () => {
                    return dirty;
                };
            }

            return Reflect.get(target, property);

        },

        // A setter trap to set the dirty flag when a member
        // variable is altered
        set(target, property, value) {
            
            // Adding or redefining functions does not constitute
            // a state change
            if(
                (target[property] && typeof target[property] !== 'function') 
                || 
                typeof value !== 'function'
            ) {
                if(target[property] !== value) dirty = true;
            }

            target[property] = value;
            return true;

        }

    });

}

此函数接受一个对象并返回一个代理,该代理捕获对象的 setter 以在代理的闭包中设置脏标志,并为脏标志定义访问器和修改器。

然后我使用此函数创建

Watchable
基类,如下所示:

// A superclass for watchable objects
class Watchable {
    constructor() {
        // Simulates an abstract class
        if (new.target === Watchable) {
            throw new Error(
                'Watchable is an abstract class and cannot be instantiated directly'
            );
        }
        return makeWatchable(this);
    }
}
然后可以扩展

Watchable
为其子类提供状态跟踪。

不幸的是,这种方法似乎存在两个重大限制:

  1. Watchable
    只会检测其状态的浅层变化。
  2. Watchable
    只会检测其 public 状态的更改。

我很乐意将第一个限制传递给客户端代码来处理。但我想支持跟踪

Watchable
子类中包含的任意私有成员,而不需要子类进行额外的工作。

因此,例如,我希望能够定义这样的子类:

class SecretRenderable extends Watchable {
    #secret;
    constructor() {
        super();
        this.#secret = 'Shh!';
    }
    setSecret(newSecret) {
        this.#secret = newSecret;
    }
}

像这样使用它:

const obj = new SecretRenderable();
obj.markClean();
obj.setSecret('SHHHHHHHHH!!!!!');

并发现

obj.isDirty()
为真。

我希望

this.#secret = newSecret
行能够被代理设置陷阱捕获。但看起来这条线完全绕过了设置的陷阱。

有没有办法可以修改我的代理实现来实现私有状态更改检测?如果没有,我应该考虑其他方法吗?

javascript es6-class private-members es6-proxy
2个回答
1
投票

正如其他人所说,这是不可能的。我怀疑它还会导致其他问题(例如:当类需要某种不影响渲染的状态时会发生什么)。

如果您想走这条路,请将渲染状态与非渲染状态分开。尝试这样的模式:

class RenderState extends Watchable {
  // add anything you think is relevant all renderable things
}

// base class for render components
class Component {
  renderState = new RenderState()
}

class MyComponent extends Component {
  constructor() {
    this.renderState.foo = "initial value for foo"
    this.stateThatDoesNotAffectRendering = 32
  }

  // this will mark us dirty
  foo() {
    ++this.renderState.foo
  }

  // this will not mark us dirty
  bar() {
    this.stateThatDoesNotAffectRendering = 44
  }
}

现在您的渲染框架可以使用

component.renderState.isDirty
来检查是否脏污。

这还有一个额外的优点,即您现在可以将所有渲染状态与非渲染状态隔离,并为您提供未来保存/加载渲染状态的灵活性。


1
投票

不,这不可能。私人会员是真正私密的,无法被代理拦截。

再次强调,唯一的解决方案是将其作为客户端问题,并记录限制。

© www.soinside.com 2019 - 2024. All rights reserved.