Proxy

  • Revision slug: JavaScript/Reference/Global_Objects/Proxy
  • Revision title: Proxy
  • Revision id: 68017
  • Created:
  • Creator: dbruant
  • Is current revision? No
  • Comment adding forwarding example; 490 words added, 1 words removed

Revision Content

{{ js_minversion_header("1.8.5") }}

{{ Non-standard_header() }}{{ draft() }}

Introduction

Proxies are objects for which the programmer has to define the semantics in JavaScript. The default object semantics are implemented in the JavaScript engine, often written in lower-level languages like C++. Proxies let the programmer fully define the behavior of an object in JavaScript. They are said to provide a meta-programming API.

Terminology

catch-all mechanism (or "intercession API")
The technical term for this feature.
handler
The object that intercepts properties.
proxy
The object whose properties are being intercepted.
proxy factory
The object or method that creates intercessive proxies.
traps
The methods that provide property access. This is analogous to the concept of traps in operating systems.

Proxies can be either be trapping or fixed.

Proxy API

Catch-alls can be defined only on distinct proxy objects, controlled by a handler object. There are two kinds of proxies:

object proxies

var proxy = Proxy.create(handler, proto);

and function proxies :

var proxy = Proxy.createFunction(handler, callTrap, constructTrap);

Where:

  • proto is an optional object representing the proxy’s prototype.
  • callTrap is a function(...args) { return any; } that reifies “proxy(...args)”. Note: The this-binding of the callTrap function is the this-binding of the call-site.
  • constructTrap is an optional function(...args) { return any-object; } that reifies “new proxy(...args)”. The this-binding of the constructTrap is undefined. If no constructTrap is provided, new proxy(...args) is reified as calling the proxy’s callTrap with this bound to a new object delegating to proxy.prototype.
  • handler is an object that implements the handler API

Handler API

There are two kinds of traps: fundamental and derived traps. If one of the fundamental traps isn't implemented and the proxy is used in a way that expects the fundamental trap to be defined, then an error is thrown. If a derived trap is implemented, then that trap's code is called whenever the corresponding behavior happens on the proxy. If the derived trap is undefined, a default implementation using the fundamental traps is used.

Fundamental traps

Emulated JavaScript code Handler method Description
Object.getOwnPropertyDescriptor(proxy, name) getOwnPropertyDescriptor : function(name) -> PropertyDescriptor | undefined  
Object.getPropertyDescriptor(proxy, name) getPropertyDescriptor: function(name) -> PropertyDescriptor | undefined This function doesn't exist in ECMAScript 5
Object.getOwnPropertyNames(proxy) getOwnPropertyNames: function() -> Strings Array  
Object.getPropertyNames(proxy) getPropertyNames: function() -> Strings Array This function doesn't exist in ECMAScript 5
Object.defineProperty(proxy,name,pd) defineProperty: function(name, propertyDescriptor) -> any  
delete proxy.name delete: function(name) -> boolean  
Object.{freeze|seal|preventExtensions}(proxy) fix: function() -> PropertyDescriptor map (indexed on property names) | undefined

After a call to fix(), the proxy becomes a regular object with the properties listed in the fix returned value and the handler stop being used. Moreover, this regular object acts as if the respective method (freeze(), seal(), or preventExtension()) had been called on it (as one would expect with a regular object).

If fix() returns undefined, the call throws a TypeError exception.

Derived traps

Default implementation refers to the code that is called for that trap when the trap isn't explicitly defined. In these functions, this refers to the handler objects.

Emulated JavaScript code Handler method Default implementation Description
name in proxy has: function(name) -> boolean
function(name) { 
return !!this.getPropertyDescriptor(name);
}
 
Object.prototype.hasOwnProperty.call(proxy, name) hasOwn: function(name) -> boolean
function(name) { 
return !!this.getOwnPropertyDescriptor(name);
}
 
proxy.name (in the context of "getting the value") get: function(proxy, name) -> any
function(receiver, name) {   var desc = this.getPropertyDescriptor(name);   if (desc === undefined) { return undefined; }   if ('value' in desc) {     return desc.value;   } else {     if (desc.get === undefined) { return undefined; }     return desc.get.call(receiver);   } }
 
proxy.name = val (in the context of "getting the value") set: function(proxy, name, val) -> boolean
function(receiver, name, val) {
  var desc = this.getOwnPropertyDescriptor(name);
  if (desc) {
    if ('writable' in desc) {
      if (desc.writable) {
        desc.value = val;
        this.defineProperty(name, desc);
        return true;
      } else {
        return false;
      }
    } else { // accessor
      if (desc.set) {
        desc.set.call(receiver, val);
        return true;
      } else {
        return false;
      }
    }
  }
  desc = this.getPropertyDescriptor(name);
  if (desc) {
    if ('writable' in desc) {
      if (desc.writable) {
        // fall through
      } else {
        return false;
      }
    } else { // accessor
      if (desc.set) {
        desc.set.call(receiver, val);
        return true;
      } else {
        return false;
      }
    }
  }
  this.defineProperty(name, {
    value: val, 
    writable: true, 
    enumerable: true, 
    configurable: true});
  return true;
}
 
for(prop in proxy){...} enumerate: function() -> String Array
function() {   return this.getPropertyNames().filter(     function (name) { return this.getPropertyDescriptor(name).enumerable }); }
From the proxy user point of view, properties appear in the for..in loop in the same order as they are in the returned array.
Object.keys(proxy) enumerate: function() -> String Array
function() {   return this.getOwnPropertyNames().filter(     function (name) { return this.getOwnPropertyDescriptor(name).enumerable }); }
 

Examples

Very simple example

A trap is called almost each time something happens to your proxy (that is used like an object). Here is an example:

var incompleteHandler = {get:function(myProxy, name){
alert('Property ' + name + ' accessed.');
return 1;
}
};

var p = Proxy(
incompleteHandler);
var q = p.blabla; // alerts 'Property blabla accessed' and 1 is assigned to q
p.azerty = "Trying to set a property"; // throws an error since neither the set trap or the fundamental trap
used in the set trap are implemented

No-op forwarding example

In this example, we are using a native JavaScript object. Our proxy will forward all its operations to the object.

function handlerMaker(obj) {
  return {
    // Fundamental traps
    getOwnPropertyDescriptor: function(name) {
      var desc = Object.getOwnPropertyDescriptor(obj, name);
      // a trapping proxy's properties must always be configurable
      if (desc !== undefined) { desc.configurable = true; }
      return desc;
    },
    getPropertyDescriptor:  function(name) {
      var desc = Object.getPropertyDescriptor(obj, name); // not in ES5
      // a trapping proxy's properties must always be configurable
      if (desc !== undefined) { desc.configurable = true; }
      return desc;
    },
    getOwnPropertyNames: function() {
      return Object.getOwnPropertyNames(obj);
    },
    getPropertyNames: function() {
      return Object.getPropertyNames(obj);                // not in ES5
    },
    defineProperty: function(name, desc) {
      Object.defineProperty(obj, name, desc);
    },
    delete:       function(name) { return delete obj[name]; },   
    fix:          function() {
      if (Object.isFrozen(obj)) {
        return Object.getOwnPropertyNames(obj).map(function(name) {
          return Object.getOwnPropertyDescriptor(obj, name);
        });
      }
      // As long as obj is not frozen, the proxy won't allow itself to be fixed
      return undefined; // will cause a TypeError to be thrown
    },
   
    // derived traps
    has:          function(name) { return name in obj; },
    hasOwn:       function(name) { return ({}).hasOwnProperty.call(obj, name); },
    get:          function(receiver, name) { return obj[name]; },
    set:          function(receiver, name, val) { obj[name] = val; return true; }, // bad behavior when set fails in non-strict mode
    enumerate:    function() {
      var result = [];
      for (name in obj) { result.push(name); };
      return result;
    },
    keys: function() { return Object.keys(obj) };
  };
}

// ...

var obj = Object.create();
var proxy = Proxy.create(handlerMaker(obj)); proxy.blabla = 12; alert(obj.blabla); // alerts 12 since the forwarding proxy has forwaded the 'set' operation to obj.

See also

Licensing note

Some content (text, examples) in this page has been copied or adapted from the ECMAScript wiki which content is licensed CC 2.0 BY-NC-SA

Revision Source

<p>{{ js_minversion_header("1.8.5") }}</p>
<p>{{ Non-standard_header() }}{{ draft() }}</p>
<h2>Introduction</h2>
<p>Proxies are objects for which the programmer has to define the semantics in JavaScript. The default object semantics are implemented in the JavaScript engine, often written in lower-level languages like C++. Proxies let the programmer fully define the behavior of an object in JavaScript. They are said to provide a <strong>meta-programming API</strong>.</p>
<h2>Terminology</h2>
<dl> <dt>catch-all mechanism (or "intercession API")</dt> <dd>The technical term for this feature.</dd> <dt>handler</dt> <dd>The object that intercepts properties.</dd> <dt>proxy</dt> <dd>The object whose properties are being intercepted.</dd> <dt>proxy factory</dt> <dd>The object or method that creates intercessive proxies.</dd> <dt>traps</dt> <dd>The methods that provide property access. This is analogous to the concept of traps in operating systems.</dd>
</dl>
<p>Proxies can be either be <strong>trapping</strong> or <strong>fixed</strong>.</p> <h2>Proxy API</h2>
<p>Catch-alls can be defined only on distinct proxy objects, controlled by a handler object. There are two kinds of proxies:</p>
<p>object proxies</p>
<pre class="brush: js"><code>var proxy = Proxy.create(handler, proto);</code></pre>
<p>and function proxies
:</p>
<pre class="brush: js"><code>var proxy = Proxy.createFunction(handler, callTrap, constructTrap);</code></pre>
<p>Where:</p>
<ul> <li>proto is an optional object representing the proxy’s prototype.</li> <li>callTrap is a function(...args) { return any; } that reifies “proxy(...args)”. Note: The this-binding of the callTrap function is the this-binding of the call-site.</li> <li>constructTrap is an optional function(...args) { return any-object; } that reifies “new proxy(...args)”. The this-binding of the constructTrap is undefined. If no constructTrap is provided, new proxy(...args) is reified as calling the proxy’s callTrap with this bound to a new object delegating to proxy.prototype.</li> <li>handler is an object that implements the handler API</li>
</ul> <h2>Handler API</h2> <p>There are two kinds of traps: <strong>fundamental</strong> and <strong>derived</strong> traps. If one of the fundamental traps isn't implemented and the proxy is used in a way that expects the fundamental trap to be defined, then an error is thrown. If a derived trap is implemented, then that trap's code is called whenever the corresponding behavior happens on the proxy. If the derived trap is undefined, a default implementation using the fundamental traps is used.</p> <h4>Fundamental traps</h4> <table class="standard-table"> <tbody> <tr> <td class="header">Emulated JavaScript code</td> <td class="header">Handler method</td> <td class="header">Description</td> </tr> <tr> <td><code>Object.getOwnPropertyDescriptor(proxy, name)</code></td> <td><code>getOwnPropertyDescriptor : function(name) -&gt; PropertyDescriptor | undefined</code></td> <td> </td> </tr> <tr> <td><code>Object.getPropertyDescriptor(proxy, name)</code></td> <td><code>getPropertyDescriptor: function(name) -&gt; PropertyDescriptor | undefined</code></td> <td>This function doesn't exist in ECMAScript 5</td> </tr> <tr> <td><code>Object.getOwnPropertyNames(proxy)</code></td> <td><code>getOwnPropertyNames: function() -&gt; Strings Array</code></td> <td> </td> </tr> <tr> <td><code>Object.getPropertyNames(proxy)</code></td> <td><code>getPropertyNames: function() -&gt; Strings Array</code></td> <td>This function doesn't exist in ECMAScript 5</td> </tr> <tr> <td><code>Object.defineProperty(proxy,name,pd)</code></td> <td><code>defineProperty: function(name, propertyDescriptor) -&gt; any</code></td> <td> </td> </tr> <tr> <td><code>delete proxy.name</code></td> <td><code>delete: function(name) -&gt; boolean</code></td> <td> </td> </tr> <tr> <td><code>Object.{freeze|seal|preventExtensions}(proxy)</code></td> <td><code>fix: function() -&gt; PropertyDescriptor map (indexed on property names) | undefined</code></td> <td> <p>After a call to <code>fix()</code>, the proxy becomes a regular object with the properties listed in the fix returned value and the handler stop being used. Moreover, this regular object acts as if the respective method (<code>freeze()</code>, <code>seal()</code>, or <code>preventExtension()</code>) had been called on it (as one would expect with a regular object).</p> <p>If <code>fix()</code> returns <code>undefined</code>, the call throws a <code>TypeError</code> exception.</p> </td> </tr> </tbody> </table> <h4>Derived traps</h4> <p>Default implementation refers to the code that is called for that trap when the trap isn't explicitly defined. In these functions, <code>this</code> refers to the handler objects.</p> <table class="standard-table"> <tbody> <tr> <td class="header">Emulated JavaScript code</td> <td class="header">Handler method</td> <td class="header">Default implementation</td> <td class="header">Description</td> </tr> <tr> <td><code>name in proxy</code></td> <td><code>has: function(name) -&gt; boolean</code></td> <td> <pre class="brush: js"><code>function(name) { <br>  return !!this.getPropertyDescriptor(name);<br>}</code></pre> </td> <td> </td> </tr> <tr> <td><code>Object.prototype.hasOwnProperty.call(proxy, name)</code></td> <td><code>hasOwn: function(name) -&gt; boolean</code></td> <td> <pre class="brush: js"><code>function(name) { <br>  return !!this.getOwnPropertyDescriptor(name); <br>}</code></pre> </td> <td> </td> </tr> <tr> <td><code>proxy.name</code> (in the context of "getting the value")</td> <td><code>get: function(proxy, name) -&gt; any</code></td> <td> <pre class="brush: js"><code><span class="kw2">function</span><span class="br0">(</span>receiver, <span class="kw3">name</span><span class="br0">)</span> <span class="br0">{</span>   <span class="kw2">var</span> desc = <span class="kw1">this</span>.<span class="me1">getPropertyDescriptor</span><span class="br0">(</span><span class="kw3">name</span><span class="br0">)</span>;   <span class="kw1">if</span> <span class="br0">(</span>desc === undefined<span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> undefined; <span class="br0">}</span>   <span class="kw1">if</span> <span class="br0">(</span><span class="st0">'value'</span> <span class="kw1">in</span> desc<span class="br0">)</span> <span class="br0">{</span>     <span class="kw1">return</span> desc.<span class="me1">value</span>;   <span class="br0">}</span> <span class="kw1">else</span> <span class="br0">{</span>     <span class="kw1">if</span> <span class="br0">(</span>desc.<span class="me1">get</span> === undefined<span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> undefined; <span class="br0">}</span>     <span class="kw1">return</span> desc.<span class="me1">get</span>.<span class="me1">call</span><span class="br0">(</span>receiver<span class="br0">)</span>;   <span class="br0">}</span> <span class="br0">}</span></code></pre> </td> <td> </td> </tr> <tr> <td><code>proxy.name = val</code> (in the context of "getting the value")</td> <td><code>set: function(proxy, name, val) -&gt; boolean</code></td> <td> <pre class="brush: js"><span class="kw2"><code>function</code></span><span class="br0">(</span>receiver, <span class="kw3">name</span>, val<span class="br0">)</span> <span class="br0">{</span>
  <span class="kw2">var</span> desc = <span class="kw1">this</span>.<span class="me1">getOwnPropertyDescriptor</span><span class="br0">(</span><span class="kw3">name</span><span class="br0">)</span>;
  <span class="kw1">if</span> <span class="br0">(</span>desc<span class="br0">)</span> <span class="br0">{</span>
    <span class="kw1">if</span> <span class="br0">(</span><span class="st0">'writable'</span> <span class="kw1">in</span> desc<span class="br0">)</span> <span class="br0">{</span>
      <span class="kw1">if</span> <span class="br0">(</span>desc.<span class="me1">writable</span><span class="br0">)</span> <span class="br0">{</span>
        desc.<span class="me1">value</span> = val;
        <span class="kw1">this</span>.<span class="me1">defineProperty</span><span class="br0">(</span><span class="kw3">name</span>, desc<span class="br0">)</span>;
        <span class="kw1">return</span> <span class="kw2">true</span>;
      <span class="br0">}</span> <span class="kw1">else</span> <span class="br0">{</span>
        <span class="kw1">return</span> <span class="kw2">false</span>;
      <span class="br0">}</span>
    <span class="br0">}</span> <span class="kw1">else</span> <span class="br0">{</span> <span class="co1">// accessor</span>
      <span class="kw1">if</span> <span class="br0">(</span>desc.<span class="me1">set</span><span class="br0">)</span> <span class="br0">{</span>
        desc.<span class="me1">set</span>.<span class="me1">call</span><span class="br0">(</span>receiver, val<span class="br0">)</span>;
        <span class="kw1">return</span> <span class="kw2">true</span>;
      <span class="br0">}</span> <span class="kw1">else</span> <span class="br0">{</span>
        <span class="kw1">return</span> <span class="kw2">false</span>;
      <span class="br0">}</span>
    <span class="br0">}</span>
  <span class="br0">}</span>
  desc = <span class="kw1">this</span>.<span class="me1">getPropertyDescriptor</span><span class="br0">(</span><span class="kw3">name</span><span class="br0">)</span>;
  <span class="kw1">if</span> <span class="br0">(</span>desc<span class="br0">)</span> <span class="br0">{</span>
    <span class="kw1">if</span> <span class="br0">(</span><span class="st0">'writable'</span> <span class="kw1">in</span> desc<span class="br0">)</span> <span class="br0">{</span>
      <span class="kw1">if</span> <span class="br0">(</span>desc.<span class="me1">writable</span><span class="br0">)</span> <span class="br0">{</span>
        <span class="co1">// fall through</span>
      <span class="br0">}</span> <span class="kw1">else</span> <span class="br0">{</span>
        <span class="kw1">return</span> <span class="kw2">false</span>;
      <span class="br0">}</span>
    <span class="br0">}</span> <span class="kw1">else</span> <span class="br0">{</span> <span class="co1">// accessor</span>
      <span class="kw1">if</span> <span class="br0">(</span>desc.<span class="me1">set</span><span class="br0">)</span> <span class="br0">{</span>
        desc.<span class="me1">set</span>.<span class="me1">call</span><span class="br0">(</span>receiver, val<span class="br0">)</span>;
        <span class="kw1">return</span> <span class="kw2">true</span>;
      <span class="br0">}</span> <span class="kw1">else</span> <span class="br0">{</span>
        <span class="kw1">return</span> <span class="kw2">false</span>;
      <span class="br0">}</span>
    <span class="br0">}</span>
  <span class="br0">}</span>
  <span class="kw1">this</span>.<span class="me1">defineProperty</span><span class="br0">(</span><span class="kw3">name</span>, <span class="br0">{</span>
    value: val, 
    writable: <span class="kw2">true</span>, 
    enumerable: <span class="kw2">true</span>, 
    configurable: <span class="kw2">true</span><span class="br0">}</span><span class="br0">)</span>;
  <span class="kw1">return</span> <span class="kw2">true</span>;
<span class="br0">}</span></pre> </td> <td> </td> </tr> <tr> <td><code>for(prop in proxy){...}</code></td> <td><code>enumerate: function() -&gt; String Array</code></td> <td> <pre class="brush: js"><code><span class="kw2">function</span><span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>   <span class="kw1">return</span> <span class="kw1">this</span>.<span class="me1">getPropertyNames</span><span class="br0">(</span><span class="br0">)</span>.<span class="me1">filter</span><span class="br0">(</span>     <span class="kw2">function</span> <span class="br0">(</span><span class="kw3">name</span><span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> <span class="kw1">this</span>.<span class="me1">getPropertyDescriptor</span><span class="br0">(</span><span class="kw3">name</span><span class="br0">)</span>.<span class="me1">enumerable</span> <span class="br0">}</span><span class="br0">)</span>; <span class="br0">}</span></code></pre> </td> <td>From the proxy user point of view, properties appear in the for..in loop in the same order as they are in the returned array.</td> </tr> <tr> <td><code>Object.keys(proxy)</code></td> <td><code>enumerate: function() -&gt; String Array</code></td> <td> <pre class="brush: js"><code><span class="kw2">function</span><span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>   <span class="kw1">return</span> <span class="kw1">this</span>.<span class="me1">getOwnPropertyNames</span><span class="br0">(</span><span class="br0">)</span>.<span class="me1">filter</span><span class="br0">(</span>     <span class="kw2">function</span> <span class="br0">(</span><span class="kw3">name</span><span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> <span class="kw1">this</span>.<span class="me1">getOwnPropertyDescriptor</span><span class="br0">(</span><span class="kw3">name</span><span class="br0">)</span>.<span class="me1">enumerable</span> <span class="br0">}</span><span class="br0">)</span>; <span class="br0">}</span></code></pre> </td> <td> </td> </tr> </tbody> </table> <h2>Examples</h2>
<h3>Very simple example</h3>
<p>A trap is called almost each time something happens to your proxy (that is used like an object). Here is an example:</p>
<pre class="brush: js"><code>var incompleteHandler = </code><code>{get:function(myProxy, name){<br>                               alert('Property ' + name + ' accessed.'); <br>                               return 1;<br>                             }<br>                        };</code><code><br>var p = Proxy(</code><code>incompleteHandler</code><code>);<br>var q = p.blabla; // alerts 'Property blabla accessed' and 1 is assigned to q<br>p.azerty = "Trying to set a property"; // throws an error since neither the set trap or the fundamental trap</code> used in the set trap are implemented
</pre>
<h3>No-op forwarding example</h3>
<p>In this example, we are using a native JavaScript object. Our proxy will forward all its operations to the object.</p>
<pre class="brush: js"><span class="kw2">function</span> handlerMaker<span class="br0">(</span>obj<span class="br0">)</span> <span class="br0">{</span>
  <span class="kw1">return</span> <span class="br0">{</span>
    // Fundamental traps
    getOwnPropertyDescriptor: <span class="kw2">function</span><span class="br0">(</span><span class="kw3">name</span><span class="br0">)</span> <span class="br0">{</span>
      <span class="kw2">var</span> desc = Object.<span class="me1">getOwnPropertyDescriptor</span><span class="br0">(</span>obj, <span class="kw3">name</span><span class="br0">)</span>;
      <span class="co1">// a trapping proxy's properties must always be configurable</span>
      <span class="kw1">if</span> <span class="br0">(</span>desc !== undefined<span class="br0">)</span> <span class="br0">{</span> desc.<span class="me1">configurable</span> = <span class="kw2">true</span>; <span class="br0">}</span>
      <span class="kw1">return</span> desc;
   <span class="br0"> }</span>,
    getPropertyDescriptor:  <span class="kw2">function</span><span class="br0">(</span><span class="kw3">name</span><span class="br0">)</span> <span class="br0">{</span>
      <span class="kw2">var</span> desc = Object.<span class="me1">getPropertyDescriptor</span><span class="br0">(</span>obj, <span class="kw3">name</span><span class="br0">)</span>; <span class="co1">// not in ES5</span>
      <span class="co1">// a trapping proxy's properties must always be configurable</span>
      <span class="kw1">if</span> <span class="br0">(</span>desc !== undefined<span class="br0">)</span> <span class="br0">{</span> desc.<span class="me1">configurable</span> = <span class="kw2">true</span>; <span class="br0">}</span>
      <span class="kw1">return</span> desc;
    <span class="br0">}</span>,
    getOwnPropertyNames: <span class="kw2">function</span><span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
      <span class="kw1">return</span> Object.<span class="me1">getOwnPropertyNames</span><span class="br0">(</span>obj<span class="br0">)</span>;
    <span class="br0">}</span>,
    getPropertyNames: <span class="kw2">function</span><span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
      <span class="kw1">return</span> Object.<span class="me1">getPropertyNames</span><span class="br0">(</span>obj<span class="br0">)</span>;                <span class="co1">// not in ES5</span>
    <span class="br0">}</span>,
    defineProperty: <span class="kw2">function</span><span class="br0">(</span><span class="kw3">name</span>, desc<span class="br0">)</span> <span class="br0">{</span>
      Object.<span class="me1">defineProperty</span><span class="br0">(</span>obj, <span class="kw3">name</span>, desc<span class="br0">)</span>;
   <span class="br0"> }</span>,
    <span class="kw1">delete</span>:       <span class="kw2">function</span><span class="br0">(</span><span class="kw3">name</span><span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> <span class="kw1">delete</span> obj<span class="br0">[</span><span class="kw3">name</span><span class="br0">]</span>; <span class="br0">}</span>,   
    fix:          <span class="kw2">function</span><span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
      <span class="kw1">if</span> <span class="br0">(</span>Object.<span class="me1">isFrozen</span><span class="br0">(</span>obj<span class="br0">)</span><span class="br0">)</span> <span class="br0">{</span>
        <span class="kw1">return</span> Object.<span class="me1">getOwnPropertyNames</span><span class="br0">(</span>obj<span class="br0">)</span>.<span class="me1">map</span><span class="br0">(</span><span class="kw2">function</span><span class="br0">(</span><span class="kw3">name</span><span class="br0">)</span> <span class="br0">{</span>
          <span class="kw1">return</span> Object.<span class="me1">getOwnPropertyDescriptor</span><span class="br0">(</span>obj, <span class="kw3">name</span><span class="br0">)</span>;
       <span class="br0"> }</span><span class="br0">)</span>;
      <span class="br0">}</span>
      <span class="co1">// As long as obj is not frozen, the proxy won't allow itself to be fixed</span>
      <span class="kw1">return</span> undefined; <span class="co1">// will cause a TypeError to be thrown</span>
   <span class="br0"> }</span>,
   
    // derived traps
    has:          <span class="kw2">function</span><span class="br0">(</span><span class="kw3">name</span><span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> <span class="kw3">name</span> <span class="kw1">in</span> obj; <span class="br0">}</span>,
    hasOwn:       <span class="kw2">function</span><span class="br0">(</span><span class="kw3">name</span><span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> <span class="br0">(</span><span class="br0">{</span><span class="br0">}</span><span class="br0">)</span>.<span class="me1">hasOwnProperty</span>.<span class="me1">call</span><span class="br0">(</span>obj, <span class="kw3">name</span><span class="br0">)</span>; <span class="br0">}</span>,
    get:          <span class="kw2">function</span><span class="br0">(</span>receiver, <span class="kw3">name</span><span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> obj<span class="br0">[</span><span class="kw3">name</span><span class="br0">]</span>; <span class="br0">}</span>,
    set:          <span class="kw2">function</span><span class="br0">(</span>receiver, <span class="kw3">name</span>, val<span class="br0">)</span> <span class="br0">{</span> obj<span class="br0">[</span><span class="kw3">name</span><span class="br0">]</span> = val; <span class="kw1">return</span> <span class="kw2">true</span>; <span class="br0">}</span>, <span class="co1">// bad behavior when set fails in non-strict mode</span>
    enumerate:    <span class="kw2">function</span><span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span>
      <span class="kw2">var</span> result = <span class="br0">[</span><span class="br0">]</span>;
      <span class="kw1">for</span> <span class="br0">(</span><span class="kw3">name</span> <span class="kw1">in</span> obj<span class="br0">)</span> <span class="br0">{</span> result.<span class="me1">push</span><span class="br0">(</span><span class="kw3">name</span><span class="br0">)</span>; <span class="br0">}</span>;
      <span class="kw1">return</span> result;
   <span class="br0"> }</span>,
    keys: <span class="kw2">function</span><span class="br0">(</span><span class="br0">)</span> <span class="br0">{</span> <span class="kw1">return</span> Object.<span class="me1">keys</span><span class="br0">(</span>obj<span class="br0">) }</span>;
  <span class="br0">}</span>;
<span class="br0">}<br><br>// ...<br><br>var obj = Object.create();<br></span><span class="kw2">var</span> proxy = Proxy.<span class="me1">create</span><span class="br0">(</span>handlerMaker<span class="br0">(</span>obj<span class="br0">)</span><span class="br0">)</span>;

proxy.blabla = 12;
alert(obj.blabla); // alerts 12 since the forwarding proxy has forwaded the 'set' operation to obj.
</pre><h2>See also</h2> <ul> <li><a class="external" href="http://jsconf.eu/2010/speaker/be_proxy_objects.html">"Proxies are awesome" Brendan Eich presentation at JSConf</a> (<a class=" external" href="http://www.slideshare.net/BrendanEich/metaprog-5303821">slides</a>)</li> <li><a class="external" href="http://wiki.ecmascript.org/doku.php?id=harmony:proxies">ECMAScript Harmony Proxy proposal page</a> and <a class=" external" href="http://wiki.ecmascript.org/doku.php?id=harmony:proxies_semantics">ECMAScript Harmony proxy semantics page</a></li> <li><a class=" external" href="http://soft.vub.ac.be/~tvcutsem/proxies/">Tutorial on proxies</a></li> </ul> <h2>Licensing note</h2> <p>Some content (text, examples) in this page has been copied or adapted from the <a class=" external" href="http://wiki.ecmascript.org/doku.php">ECMAScript wiki</a> which content is licensed <a class=" external" href="http://creativecommons.org/licenses/by-nc-sa/2.0/">CC 2.0 BY-NC-SA</a></p>
Revert to this revision