title: Why does Proxy break this-binding?
body: <p>I'm trying to proxy a set of object such that I can pass them off to third party code and temporarily nullify mutation methods and setters, and then revoke the proxy handler traps to reinstate normal behaviour. What I've discovered is that proxies are inherently hostile to <code>this</code>-dependent code.</p>
  <p>I'm intrigued as to how &amp; why Javascript Proxies break <code>this</code> binding for their proxied targets. In the following example I have a simple class which ingests a value on construction, stores it as a private field, and returns it on property access. Observe that:</p>
  <ol>
  <li>Attempting to access the property through a proxy without a handler throws an error</li>
  <li>Explicitly forwarding a handler <code>get</code> trap through <code>Reflect.get</code> reinstates normal behaviour</li>
  </ol>
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>class Thing {
    #value
  
    constructor(value){
      this.#value = value
    }
    
    get value(){
      return this.#value
    }
  }
  
  // Default behaviour
  const thing1 = new Thing('foo')
  
  attempt(() =&gt; thing1.value)
  
  // No-op proxy breaks contextual access behaviour
  const proxy1 = new Proxy(thing1, {})
  
  attempt(() =&gt; proxy1.value)
  
  // Reinstated by explicitly forwarding handler get call to Reflect
  const proxy2 = new Proxy(thing1, {get: (target, key) =&gt;
    Reflect.get(target, key)
  })
  
  attempt(() =&gt; proxy2.value)
  
  function attempt(fn){
    try {
      console.log(fn())
    }
    catch(e){
      console.error(e)
    }
  }</code></pre>
  </div>
  </div>
  </p>
  <p>This offers a workaround for getter access, but I don't understand why the problem arises or why the extra code fixes it. The same problem of context clash in unhandled proxy queries is more vexing when it comes to methods. In the following code, the <code>value</code> property becomes a method rather than a getter. In this case:</p>
  <ol>
  <li>Default behaviour is still broken</li>
  <li><code>Reflect.get</code> no longer works</li>
  <li>I <em>can</em> explicitly bind <code>this</code> in the <code>get</code> trap</li>
  <li>But this isn't a reinstatement of expected behaviour</li>
  </ol>
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>class Thing {
    #value
  
    constructor(value){
      this.#value = value
    }
    
    value(){
      return this.#value
    }
  }
  
  // Default behaviour
  const thing1 = new Thing('foo')
  
  attempt(() =&gt; thing1.value())
  
  // No-op proxy breaks contextual access behaviour
  const proxy1 = new Proxy(thing1, {})
  
  attempt(() =&gt; proxy1.value())
  
  // Forwarding handler get trap to Reflect doesn't work
  const proxy2 = new Proxy(thing1, {get: (target, key) =&gt;
    Reflect.get(target, key)
  })
  
  attempt(() =&gt; proxy2.value())
  
  // Explicitly binding the returned method *does* work
  const proxy3 = new Proxy(thing1, {get: (target, key) =&gt;
    target[key].bind(target)
  })
  
  attempt(() =&gt; proxy3.value())
  
  // But this goes beyond reinstating normal behaviour
  var {value} = thing1
  
  attempt(() =&gt; value())
  
  var {value} = proxy3
  
  attempt(() =&gt; value())
  
  function attempt(fn){
    try {
      console.log(fn())
    }
    catch(e){
      console.error(e)
    }
  }</code></pre>
  </div>
  </div>
  </p>