Skip to content

Instantly share code, notes, and snippets.

@Kambfhase
Created March 23, 2011 12:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Kambfhase/883016 to your computer and use it in GitHub Desktop.
Save Kambfhase/883016 to your computer and use it in GitHub Desktop.
A JavaScript Proxy for Fibonacci numbers.
// a fibonacci numbers object using proxy.
var fib = Object.create( Proxy.create({
getPropertyDescriptor: function( name){
var index = ~~name;
if( index == name && index > 1){
return {
get: function(){
var val = this[ index-1] + this[ index-2];
Object.defineProperty( this, index, {
value: val,
enumerable: true,
writable: true,
configurable: true
});
return val;
}
};
} else {
return Object.getOwnPropertyDescriptor( Object.prototype, name);
}
},
getOwnPropertyDescriptor: function( name){
return this.getPropertyDescriptor( name);
},
getPropertyNames: function(){
return [];
},
getOwnPropertyNames: function(){
return [];
},
delete:function(){
return false;
},
fix: function(){
return;
}
}), {
0: {
value: 0,
enumerable: true,
writable: true,
configurable: true
},
1: {
value: 1,
enumerable: true,
writable: true,
configurable: true
}
});

A Fibonacci Proxy

So Firefox 4 introduces the ECMAScript Harmony Proxies. They are total mindfuck. This is just another tutorial to help you wrap your head around them. But first a word of warning: Using proxies you can easily create infinite loops or recursion. With missing traps you will get a lot of TypeErrors thrown at you. Whenever you inspect an object like the proxy in your console( Firebug or whatever) some methods will be auto called and most likely fail. Don't let that bring you down. Keep on hacking!

fibonacci numbers.

In this tutorial we create an object called fib that contains the fibonacci sequence. eg. fib[0] is 0, fib[1] is 1 and fib[5] is 5. See http://en.wikipedia.org/wiki/Fibonacci_number

the fib object

You might want to make sure you understand JS prototypal inheritance and ES5 property descriptors. If you are comfortable with it, the following code should be easy for you:

var fib = Object.create( { /* magic */ }, {
    0: {
        value: 0,
        enumerable: true,
        writable: true,
        configurable: true
    },
    1: {
        value: 1,
        enumerable: true,
        writable: true,
        configurable: true
    }
});

fib now has two own properties, 0 and 1. The value of enumerable etc. doesn't matter, I just set them to true for convenience.

the magic proxy

When looking up a property on an object the JS engine always starts at the top of the prototype chain. So if you try to get the 0 property of fib you get 0 because fib has an own property with the key 0. But since fib has no 5 the JS engine will go up the prototype chain and look there for the 5. In this case it will check if the 5 exists on our magic object. Using proxies we can catch this lookup by using the getPropertyDescriptor trap:

var fib = Object.create( Proxy.create({ 
    getPropertyDescriptor: function( name){
        var index = ~~name;
        if( index == name && index > 1){
            return {
                /* ... */
            };
        } else {
            return Object.getOwnPropertyDescriptor( Object.prototype, name);
        }
    }
}), {
     /* ... */
});

So when you try to access fib[5] the getPropertyDescriptor trap is called with the parameter "5". Note that the 5 is a string! So we convert that string into an int and check if they are "equal". It told you that creating infinite loops is easy. Now think what would happen if we did not check that index is bigger than 1 and try to access -1. If the given name was not a number lookup the property on Object.prototype. This allows us to work with fib as if it were just a regular object.

the getting setter

Onto the tricky part. First thing to note is that the proxy has no direct access to the object it is trapping. ie: it does not know fib, nor the prototype of fib. The this parameter of a trap is always the trap object.

return {
    get: function(){
        var val = this[ index-1] + this[ index-2];
        Object.defineProperty( this, index, {
            value: val,
            enumerable: true,
            writable: true,
            configurable: true
        });
        return val;
    }
};

Here we return a getter. In fact, a getters this points to the object you try to get form, which in this case is fib. Yes, even though the getter is a virtual property of fib it has access to fib. Next thing we do is get this index two predecessors and calculate the current fibonacci number. After that we define a new property on fib with the key index and the value val. This is an own property ie. it lives on fib, not the proxy. So if we ask for it again later the proxy will not be notified, but we will have the correct value right away. At last val is returned.

Some example usage:

fib[5] // 5
fib.hasOwnProperty(10) // false
fib[10] // 55
fib.hasOwnProperty(10) // true

To wrap things up we implement empty traps for the rest.

Fin

So now we got it: an object whose properties are the fibonacci numbers. Now go on and fiddle with it. Try iterating over it, or change the Object.prototype to Array.prototype.

I hope you understand proxies now or at least have an idea of how powerful they are.

Suggestions are always welcome. You can find me at twitter @Kambfhase or on github.

mfG Hase

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment