How to use Proxy to simulate PHP's __callStatic attribute in Node.js
P粉653045807
P粉653045807 2023-09-04 09:05:21
0
1
521
<p>I'm trying to create the same behavior in Node.js as the PHP <code>__callStatic</code> magic method. </p> <p>I'm trying to do it using <code>Proxy</code> but I'm not sure if it's the best option. </p> <p> <pre class="snippet-code-js lang-js prettyprint-override"><code>class Test { constructor() { this.num = 0 } set(num) { this.num = this.num num return this } get() { return this.num } } const TestFacade = new Proxy({}, { get: (_, key) => { const test = new Test() return test[key] } }) //The execution method chain ends with get console.log(TestFacade.set(10).set(20).get()) //Expected result: 30 //Return result: 0 // Start a new execution method chain and instantiate the Test class again in the first set console.log(TestFacade.set(20).set(20).get()) //Expected result: 40 // Return result: 0</code></pre> </p> <p>The problem is that every time I try to access the properties of <code>TestFacade</code>, the <code>get</code> trap is triggered. The behavior I need is that when the <code>set</code> method is called it will return <code>this</code> of the <code>Test</code> class and I can even save that instance for later use! </p> <pre class="brush:php;toolbar:false;">const testInstance = TestFacade.set(10) // The set method returns `this` of `Test` instead of Proxy</pre> <p>If anything is unclear, please let me know. </p>
P粉653045807
P粉653045807

reply all(1)
P粉549986089

I don't know if this is the best option. But I solved the problem by returning a new proxy in the get trap that binds the test class instance into the method using the apply trap :

class Facade {
  static #facadeAccessor

  static createFacadeFor(provider) {
    this.#facadeAccessor = provider

    return new Proxy(this, { get: this.__callStatic.bind(this) })
  }

  static __callStatic(facade, key) {
    /**
     * 访问Facade类的方法而不是提供者的方法。
     */
    if (facade[key]) {
      return facade[key]
    }

    const provider = new this.#facadeAccessor()

    const apply = (method, _this, args) => method.bind(provider)(...args)

    if (provider[key] === undefined) {
      return undefined
    }

    /**
     * 访问类的属性。
     */
    if (typeof provider[key] !== 'function') {
      return provider[key]
    }

    return new Proxy(provider[key], { apply })
  }
}

class Test {
  num = 0

  set(num) {
    this.num = this.num + num

    return this
  }

  get() {
    return this.num
  }
}

const TestFacade = Facade.createFacadeFor(Test)

console.log(TestFacade.set(10).set(20).get()) // 30
console.log(TestFacade.set(5).set(5).get()) // 10

const testInstance = TestFacade.set(10)

console.log(testInstance.num) // 10
console.log(testInstance.get()) // 10
console.log(testInstance.set(10).get()) // 20
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template