Learning Advanced JavaScript


    Double-click the code to edit the tutorial and try your own code.
    This tutorial contains code and discussion from the upcoming book Secrets of the JavaScript Ninja by John Resig.

    Our Goal
    Goal: To be able to understand this function:
    // The .bind method from Prototype.js
     Function.prototype.bind = function(){ 
       var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift(); 
       return function(){ 
         return fn.apply(object, 
           args.concat(Array.prototype.slice.call(arguments))); 
       }; 
     }; 
    Some helper methods that we have:
    assert( true, "I'll pass." );
    assert( "truey", "So will I." );
    assert( false, "I'll fail." );
    assert( null, "So will I." );
    log( "Just a simple log", "of", "values.", true );
    error( "I'm an error!" );
    Defining Functions
    What ways can we define functions?
     function isNimble(){ return true; } 
     var canFly = function(){ return true; }; 
     window.isDeadly = function(){ return true; }; 
     log(isNimble, canFly, isDeadly); 
    Does the order of function definition matter?
     var canFly = function(){ return true; }; 
     window.isDeadly = function(){ return true; }; 
     assert( isNimble() && canFly() && isDeadly(), "Still works, even though isNimble is moved." ); 
     function isNimble(){ return true; } 
    Where can assignments be accessed?
     assert( typeof canFly == "undefined", "canFly doesn't get that benefit." ); 
     assert( typeof isDeadly == "undefined", "Nor does isDeadly." ); 
     var canFly = function(){ return true; }; 
     window.isDeadly = function(){ return true; }; 
    Can functions be defined below return statements?
     function stealthCheck(){ 
       assert( stealth(), "We'll never get below the return, but that's OK!" ); 
    
       return stealth();
    
       function stealth(){ return true; } 
     } 
    
     stealthCheck(); 
    Named Functions
    We can refer to a function, within itself, by its name.
     function yell(n){ 
       return n > 0 ? yell(n-1) + "a" : "hiy"; 
     } 
     assert( yell(4) == "hiyaaaa", "Calling the function by itself comes naturally." ); 
    What is the name of a function?
     var ninja = function myNinja(){ 
       assert( ninja == myNinja, "This function is named two things - at once!" ); 
     }; 
     ninja(); 
     assert( typeof myNinja == "undefined", "But myNinja isn't defined outside of the function." ); 
     log( ninja );
    We can even do it if we're an anonymous function that's an object property.
     var ninja = { 
       yell: function(n){ 
         return n > 0 ? ninja.yell(n-1) + "a" : "hiy"; 
       } 
     }; 
     assert( ninja.yell(4) == "hiyaaaa", "A single object isn't too bad, either." ); 
    But what happens when we remove the original object?
     var ninja = { 
       yell: function(n){ 
         return n > 0 ? ninja.yell(n-1) + "a" : "hiy"; 
       } 
     }; 
     assert( ninja.yell(4) == "hiyaaaa", "A single object isn't too bad, either." ); 
    
     var samurai = { yell: ninja.yell }; 
     var ninja = null; 
      
     try { 
       samurai.yell(4); 
     } catch(e){ 
       assert( false, "Uh, this isn't good! Where'd ninja.yell go?" ); 
     } 
    Let's give the anonymous function a name!
     var ninja = { 
       yell: function yell(n){ 
         return n > 0 ? yell(n-1) + "a" : "hiy"; 
       } 
     }; 
     assert( ninja.yell(4) == "hiyaaaa", "Works as we would expect it to!" ); 
      
     var samurai = { yell: ninja.yell }; 
     var ninja = {}; 
     assert( samurai.yell(4) == "hiyaaaa", "The method correctly calls itself." ); 
    What if we don't want to give the function a name?
     var ninja = { 
       yell: function(n){ 
         return n > 0 ? arguments.callee(n-1) + "a" : "hiy"; 
       } 
     }; 
     assert( ninja.yell(4) == "hiyaaaa", "arguments.callee is the function itself." ); 
    Functions as Objects
    How similar are functions and objects?
     var obj = {}; 
     var fn = function(){}; 
     assert( obj && fn, "Both the object and function exist." ); 
    How similar are functions and objects?
     var obj = {}; 
     var fn = function(){}; 
     obj.prop = "some value"; 
     fn.prop = "some value"; 
     assert( obj.prop == fn.prop, "Both are objects, both have the property." ); 
    Is it possible to cache the return results from a function?
     function getElements( name ) {
       var results;
    
       if ( getElements.cache[name] ) {
         results = getElements.cache[name];
       } else {
         results = document.getElementsByTagName(name);
         getElements.cache[name] = results;
       }
    
       return results;
     } 
     getElements.cache = {};
    
     log( "Elements found: ", getElements("pre").length );
     log( "Cache found: ", getElements.cache.pre.length ); 
    QUIZ: Can you cache the results of this function?
     function isPrime( num ) { 
       var prime = num != 1; // Everything but 1 can be prime 
       for ( var i = 2; i < num; i++ ) { 
         if ( num % i == 0 ) { 
           prime = false; 
           break; 
         } 
       } 
       return prime; 
     } 
      
     assert( isPrime(5), "Make sure the function works, 5 is prime." ); 
     assert( isPrime.cache[5], "Is the answer cached?" );
    One possible way to cache the results:
     function isPrime( num ) { 
       if ( isPrime.cache[ num ] != null ) 
         return isPrime.cache[ num ]; 
        
       var prime = num != 1; // Everything but 1 can be prime 
       for ( var i = 2; i < num; i++ ) { 
         if ( num % i == 0 ) { 
           prime = false; 
           break; 
         } 
       }
       
       isPrime.cache[ num ] = prime
       
       return prime; 
     } 
     
     isPrime.cache = {}; 
      
     assert( isPrime(5), "Make sure the function works, 5 is prime." ); 
     assert( isPrime.cache[5], "Make sure the answer is cached." ); 
    Context
    What happens if a function is an object property?
     var katana = { 
       isSharp: true, 
       use: function(){ 
         this.isSharp = !this.isSharp; 
       } 
     }; 
     katana.use();
     assert( !katana.isSharp, "Verify the value of isSharp has been changed." );
    What exactly does context represent?
     function katana(){ 
       this.isSharp = true; 
     } 
     katana(); 
     assert( isSharp === true, "A global object now exists with that name and value." ); 
      
     var shuriken = { 
       toss: function(){ 
         this.isSharp = true; 
       } 
     }; 
     shuriken.toss(); 
     assert( shuriken.isSharp === true, "When it's an object property, the value is set within the object." ); 
    How can we change the context of a function?
     var object = {}; 
     function fn(){ 
       return this; 
     } 
     assert( fn() == this, "The context is the global object." ); 
     assert( fn.call(object) == object, "The context is changed to a specific object." ); 
    Different ways of changing the context:
     function add(a, b){ 
       return a + b; 
     } 
     assert( add.call(this, 1, 2) == 3, ".call() takes individual arguments" ); 
     assert( add.apply(this, [1, 2]) == 3, ".apply() takes an array of arguments" ); 
    QUIZ: How can we implement looping with a callback?
     function loop(array, fn){ 
       for ( var i = 0; i < array.length; i++ ) {
         // Implement me!
       }
     } 
     var num = 0; 
     loop([0, 1, 2], function(value){ 
       assert(value == num++, "Make sure the contents are as we expect it."); 
       assert(this instanceof Array, "The context should be the full array.");
     }); 
    A possible solution for function looping:
     function loop(array, fn){ 
       for ( var i = 0; i < array.length; i++ ) 
         fn.call( array, array[i], i );
     } 
     var num = 0; 
     loop([0, 1, 2], function(value, i){ 
       assert(value == num++, "Make sure the contents are as we expect it."); 
       assert(this instanceof Array, "The context should be the full array.");
     }); 
    Instantiation
    What does the new operator do?
     function Ninja(){
       this.name = "Ninja";
     } 
      
     var ninjaA = Ninja(); 
     assert( !ninjaA, "Is undefined, not an instance of Ninja." ); 
      
     var ninjaB = new Ninja(); 
     assert( ninjaB.name == "Ninja", "Property exists on the ninja instance." ); 
    We have a 'this' context that is a Ninja object.
     function Ninja(){ 
       this.swung = false; 
        
       // Should return true 
       this.swingSword = function(){ 
         this.swung = !this.swung; 
         return this.swung;
       }; 
     } 
      
     var ninja = new Ninja(); 
     assert( ninja.swingSword(), "Calling the instance method." ); 
     assert( ninja.swung, "The ninja has swung the sword." );
     
     var ninjaB = new Ninja();
     assert( !ninjaB.swung, "Make sure that the ninja has not swung his sword." );
    QUIZ: Add a method that gives a name to the ninja.
     function Ninja(name){
       // Implement!
    }
    
    var ninja = new Ninja("John");
    assert( ninja.name == "John", "The name has been set on initialization" );
    
    ninja.changeName("Bob");
    assert( ninja.name == "Bob", "The name was successfully changed." );
    Add a new property and method to the object.
     function Ninja(name){
       this.changeName = function(name){
         this.name = name;
       };
    
       this.changeName( name );
    }
    
    var ninja = new Ninja("John");
    assert( ninja.name == "John", "The name has been set on initialization" );
    
    ninja.changeName("Bob");
    assert( ninja.name == "Bob", "The name was successfully changed." );
    What happens when we forget to use the new operator?
    function User(first, last){ 
       this.name = first + " " + last; 
     } 
      
     var user = User("John", "Resig"); 
     assert( typeof user == "undefined", "Since new wasn't used, the instance is undefined." ); 
    What happens when we forget to use the new operator? (cont.)
     function User(first, last){ 
       this.name = first + " " + last; 
     } 
      
     window.name = "Resig"; 
     var user = User("John", name); 
      
     assert( name == "John Resig", "The name variable is accidentally overridden." ); 
    We need to make sure that the new operator is always used.
     function User(first, last){ 
       if ( !(this instanceof User) ) 
         return new User(first, last); 
        
       this.name = first + " " + last; 
     } 
      
     var name = "Resig"; 
     var user = User("John", name); 
      
     assert( user, "This was defined correctly, even if it was by mistake." ); 
     assert( name == "Resig", "The right name was maintained." ); 
    QUIZ: Is there another, more generic, way of doing this?
     function User(first, last){ 
       if ( !(this instanceof ___) ) 
         return new User(first, last); 
        
       this.name = first + " " + last; 
     } 
      
     var name = "Resig"; 
     var user = User("John", name); 
      
     assert( user, "This was defined correctly, even if it was by mistake." ); 
     assert( name == "Resig", "The right name was maintained." ); 
    A solution using arguments.callee.
     function User(first, last){ 
       if ( !(this instanceof arguments.callee) ) 
         return new User(first, last); 
        
       this.name = first + " " + last; 
     } 
      
     var name = "Resig"; 
     var user = User("John", name); 
      
     assert( user, "This was defined correctly, even if it was by mistake." ); 
     assert( name == "Resig", "The right name was maintained." ); 
    Flexible Arguments
    Using a variable number of arguments to our advantage.
     function merge(root){ 
       for ( var i = 1; i < arguments.length; i++ ) 
         for ( var key in arguments[i] ) 
           root[key] = arguments[i][key]; 
       return root; 
     } 
      
     var merged = merge({name: "John"}, {city: "Boston"}); 
     assert( merged.name == "John", "The original name is intact." ); 
     assert( merged.city == "Boston", "And the city has been copied over." ); 
    How can we find the Min/Max number in an array?
     function smallest(array){ 
       return Math.min.apply( Math, array ); 
     } 
     function largest(array){ 
       return Math.max.apply( Math, array ); 
     } 
     assert(smallest([0, 1, 2, 3]) == 0, "Locate the smallest value."); 
     assert(largest([0, 1, 2, 3]) == 3, "Locate the largest value."); 
    Another possible solution:
     function smallest(){ 
       return Math.min.apply( Math, arguments ); 
     } 
     function largest(){ 
       return Math.max.apply( Math, arguments ); 
     } 
     assert(smallest(0, 1, 2, 3) == 0, "Locate the smallest value."); 
     assert(largest(0, 1, 2, 3) == 3, "Locate the largest value."); 
    Uh oh, what's going wrong here?
     function highest(){ 
       return arguments.sort(function(a,b){
         return b - a;
       });
     } 
     assert(highest(1, 1, 2, 3)[0] == 3, "Get the highest value."); 
     assert(highest(3, 1, 2, 3, 4, 5)[1] == 4, "Verify the results."); 
    QUIZ: We must convert array-like objects into actual arrays. Can any built-in methods help?
     // Hint: Arrays have .slice and .splice methods which return new arrays.
    function highest(){ 
       return makeArray(arguments).slice(1).sort(function(a,b){
         return b - a;
       });
     } 
    
     function makeArray(array){
       // Implement me!
     }
    
     // Expecting: [3,2,1]
     assert(highest(1, 1, 2, 3)[0] == 3, "Get the highest value."); 
     // Expecting: [5,4,3,2,1]
     assert(highest(3, 1, 2, 3, 4, 5)[1] == 4, "Verify the results."); 
    We can use built-in methods to our advantage.
     function highest(){ 
       return makeArray(arguments).sort(function(a,b){
         return b - a;
       });
     } 
    
     function makeArray(array){
       return Array().slice.call( array );
     }
    
     assert(highest(1, 1, 2, 3)[0] == 3, "Get the highest value."); 
     assert(highest(3, 1, 2, 3, 4, 5)[1] == 4, "Verify the results."); 
    QUIZ: Implement a multiplication function (first argument by largest number).
     function multiMax(multi){ 
       // Make an array of all but the first argument
       var allButFirst = ___;
    
       // Find the largest number in that array of arguments
       var largestAllButFirst = ___;
    
       // Return the multiplied result
       return multi * largestAllButFirst;
     } 
     assert( multiMax(3, 1, 2, 3) == 9, "3*3=9 (First arg, by largest.)" ); 
    We can use call and apply to build a solution.
     function multiMax(multi){ 
       // Make an array of all but the first argument
       var allButFirst = Array().slice.call( arguments, 1 );
    
       // Find the largest number in that array of arguments
       var largestAllButFirst = Math.max.apply( Math, allButFirst );
    
       // Return the multiplied result
       return multi * largestAllButFirst;
     } 
     assert( multiMax(3, 1, 2, 3) == 9, "3*3=9 (First arg, by largest.)" ); 
    Closures
    A basic closure.
     var num = 10;
    
     function addNum(myNum){
       return num + myNum;
     }
    
     assert( addNum(5) == 15, "Add two numbers together, one from a closure." );
    But why doesn't this work?
     var num = 10;
    
     function addNum(myNum){
       return num + myNum;
     }
     
     num = 15;
    
     assert( addNum(5) == 15, "Add two numbers together, one from a closure." );
    Closures are frequently used for callbacks.
     var results = jQuery("#results").html("<li>Loading...</li>"); 
    
     jQuery.get("test.html", function(html){ 
       results.html( html ); 
       assert( results, "The element to append to, via a closure." ); 
     }); 
    They're also useful for timers.
     var count = 0; 
      
     var timer = setInterval(function(){ 
       if ( count < 5 ) { 
         log( "Timer call: ", count );
         count++; 
       } else { 
         assert( count == 5, "Count came via a closure, accessed each step." ); 
         assert( timer, "The timer reference is also via a closure." ); 
         clearInterval( timer ); 
       } 
     }, 100); 
    and they're also frequently used when attaching event listeners.
     var count = 1;
     var elem = document.createElement("li");
     elem.innerHTML = "Click me!";
     elem.onclick = function(){
       log( "Click #", count++ );
     };
     document.getElementById("results").appendChild( elem );
     assert( elem.parentNode, "Clickable element appended." );
    Private properties, using closures.
     function Ninja(){ 
       var slices = 0; 
        
       this.getSlices = function(){ 
         return slices; 
       }; 
       this.slice = function(){ 
         slices++; 
       }; 
     } 
      
     var ninja = new Ninja(); 
     ninja.slice(); 
     assert( ninja.getSlices() == 1, "We're able to access the internal slice data." ); 
     assert( ninja.slices === undefined, "And the private data is inaccessible to us." ); 
    QUIZ: What are the values of the variables?
    var a = 5;
    function runMe(a){
      assert( a == ___, "Check the value of a." );
    
      function innerRun(){
        assert( b == ___, "Check the value of b." );
        assert( c == ___, "Check the value of c." );
      }
    
      var b = 7;
      innerRun();
      var c = 8;
    }
    runMe(6);
    
    for ( var d = 0; d < 3; d++ ) {
      setTimeout(function(){
        assert( d == ___, "Check the value of d." );
      }, 100);
    }
    The last one is quite tricky, we'll revisit it.
    var a = 5;
    function runMe(a){
      assert( a == 6, "Check the value of a." );
    
      function innerRun(){
        assert( b == 7, "Check the value of b." );
        assert( c == undefined, "Check the value of c." );
      }
    
      var b = 7;
      innerRun();
      var c = 8;
    }
    runMe(6);
    
    for ( var d = 0; d < 3; d++ ) {
      setTimeout(function(){
        assert( d == 3, "Check the value of d." );
      }, 100);
    }
    Temporary Scope
    Self-executing, temporary, function
     (function(){
       var count = 0; 
     
       var timer = setInterval(function(){ 
         if ( count < 5 ) { 
           log( "Timer call: ", count ); 
           count++; 
         } else { 
           assert( count == 5, "Count came via a closure, accessed each step." ); 
           assert( timer, "The timer reference is also via a closure." ); 
           clearInterval( timer ); 
         } 
       }, 100);
    })();
    
    assert( typeof count == "undefined", "count doesn't exist outside the wrapper" );
    assert( typeof timer == "undefined", "neither does timer" );
    Now we can handle closures and looping.
    for ( var d = 0; d < 3; d++ ) (function(d){
      setTimeout(function(){
        log( "Value of d: ", d );
        assert( d == d, "Check the value of d." );
      }, d * 200);
    })(d);
    The anonymous wrapper functions are also useful for wrapping libraries.
     (function(){ 
       var myLib = window.myLib = function(){ 
         // Initialize 
       }; 
      
       // ... 
     })(); 
    Another way to wrap a library:
     var myLib = (function(){ 
       function myLib(){ 
         // Initialize 
       } 
      
       // ... 
        
       return myLib; 
     })(); 
    QUIZ: Fix the broken closures in this loop!
     var count = 0;
     for ( var i = 0; i < 4; i++ ) {
       setTimeout(function(){
         assert( i == count++, "Check the value of i." );
       }, i * 200);
     }
    A quick wrapper function will do the trick.
     var count = 0;
     for ( var i = 0; i < 4; i++ ) (function(i){
       setTimeout(function(){
         assert( i == count++, "Check the value of i." );
       }, i * 200);
     })(i);
    Function Prototypes
    Adding a prototyped method to a function.
     function Ninja(){} 
      
     Ninja.prototype.swingSword = function(){ 
       return true; 
     }; 
      
     var ninjaA = Ninja(); 
     assert( !ninjaA, "Is undefined, not an instance of Ninja." ); 
      
     var ninjaB = new Ninja(); 
     assert( ninjaB.swingSword(), "Method exists and is callable." ); 
    Properties added in the constructor (or later) override prototyped properties.
     function Ninja(){ 
       this.swingSword = function(){ 
         return true; 
       }; 
     } 
      
     // Should return false, but will be overridden 
     Ninja.prototype.swingSword = function(){ 
       return false; 
     }; 
      
     var ninja = new Ninja(); 
     assert( ninja.swingSword(), "Calling the instance method, not the prototype method." );
    Prototyped properties affect all objects of the same constructor, simultaneously, even if they already exist.
     function Ninja(){ 
       this.swung = true; 
     } 
      
     var ninjaA = new Ninja(); 
     var ninjaB = new Ninja(); 
      
     Ninja.prototype.swingSword = function(){ 
       return this.swung; 
     }; 
      
     assert( ninjaA.swingSword(), "Method exists, even out of order." );
     assert( ninjaB.swingSword(), "and on all instantiated objects." ); 
    QUIZ: Make a chainable Ninja method.
     function Ninja(){ 
       this.swung = true; 
     } 
      
     var ninjaA = new Ninja(); 
     var ninjaB = new Ninja(); 
      
     // Add a method to the Ninja prototype which
     // returns itself and modifies swung
      
     assert( !ninjaA.swing().swung, "Verify that the swing method exists and returns an instance." );
     assert( !ninjaB.swing().swung, "and that it works on all Ninja instances." ); 
    The chainable method must return this.
     function Ninja(){ 
       this.swung = true; 
     } 
      
     var ninjaA = new Ninja(); 
     var ninjaB = new Ninja(); 
     
     Ninja.prototype.swing = function(){
       this.swung = false;
       return this;
     }; 
      
     assert( !ninjaA.swing().swung, "Verify that the swing method exists and returns an instance." );
     assert( !ninjaB.swing().swung, "and that it works on all Ninja instances." ); 
    Instance Type
    Examining the basics of an object.
     function Ninja(){} 
      
     var ninja = new Ninja(); 
    
     assert( typeof ninja == "object", "However the type of the instance is still an object." );   
     assert( ninja instanceof Ninja, "The object was instantiated properly." ); 
     assert( ninja.constructor == Ninja, "The ninja object was created by the Ninja function." ); 
    We can still use the constructor to build other instances.
     function Ninja(){}
     var ninja = new Ninja(); 
     var ninjaB = new ninja.constructor(); 
      
     assert( ninjaB instanceof Ninja, "Still a ninja object." ); 
    QUIZ: Make another instance of a Ninja.
    var ninja = (function(){
      function Ninja(){}
      return new Ninja();
    })();
    
    // Make another instance of Ninja
    var ninjaB = ___;
    
    assert( ninja.constructor == ninjaB.constructor, "The ninjas come from the same source." );
    QUIZ: Use the .constructor property to dig in.
    var ninja = (function(){
      function Ninja(){}
      return new Ninja();
    })();
    
    // Make another instance of Ninja
    var ninjaB = new ninja.constructor();
    
    assert( ninja.constructor == ninjaB.constructor, "The ninjas come from the same source." );
    Inheritance
    The basics of how prototypal inheritance works.
     function Person(){} 
     Person.prototype.dance = function(){}; 
      
     function Ninja(){} 
      
     // Achieve similar, but non-inheritable, results 
     Ninja.prototype = Person.prototype; 
     Ninja.prototype = { dance: Person.prototype.dance }; 
    
     assert( (new Ninja()) instanceof Person, "Will fail with bad prototype chain." );
      
     // Only this maintains the prototype chain 
     Ninja.prototype = new Person(); 
      
     var ninja = new Ninja(); 
     assert( ninja instanceof Ninja, "ninja receives functionality from the Ninja prototype" ); 
     assert( ninja instanceof Person, "... and the Person prototype" ); 
     assert( ninja instanceof Object, "... and the Object prototype" ); 
    QUIZ: Let's try our hand at inheritance.
     function Person(){}
     Person.prototype.getName = function(){
       return this.name;
     };
    
     // Implement a function that inherits from Person
     // and sets a name in the constructor
    
     var me = new Me();
     assert( me.getName(), "A name was set." );
    The result is rather straight-forward.
     function Person(){}
     Person.prototype.getName = function(){
       return this.name;
     };
    
     function Me(){
       this.name = "John Resig";
     }
     Me.prototype = new Person();
    
     var me = new Me();
     assert( me.getName(), "A name was set." );
    Built-in Prototypes
    We can also modify built-in object prototypes.
     if (!Array.prototype.forEach) { 
       Array.prototype.forEach = function(fn){ 
         for ( var i = 0; i < this.length; i++ ) { 
           fn( this[i], i, this ); 
         } 
       }; 
     } 
      
     ["a", "b", "c"].forEach(function(value, index, array){ 
       assert( value, "Is in position " + index + " out of " + (array.length - 1) ); 
     }); 
    Beware: Extending prototypes can be dangerous.
     Object.prototype.keys = function(){ 
       var keys = []; 
       for ( var i in this ) 
         keys.push( i ); 
       return keys; 
     }; 
      
     var obj = { a: 1, b: 2, c: 3 }; 
    
     assert( obj.keys().length == 3, "We should only have 3 properties." );
    
     delete Object.prototype.keys;
    Enforcing Function Context
    What happens when we try to bind an object's method to a click handler?
     var Button = { 
       click: function(){ 
         this.clicked = true; 
       } 
     }; 
      
     var elem = document.createElement("li");
     elem.innerHTML = "Click me!";
     elem.onclick = Button.click;
     document.getElementById("results").appendChild(elem);
    
     elem.onclick();
     assert( elem.clicked, "The clicked property was accidentally set on the element" ); 
    We need to keep its context as the original object.
     function bind(context, name){ 
       return function(){ 
         return context[name].apply(context, arguments); 
       }; 
     } 
    
     var Button = { 
       click: function(){ 
         this.clicked = true; 
       } 
     }; 
      
     var elem = document.createElement("li");
     elem.innerHTML = "Click me!";
     elem.onclick = bind(Button, "click");
     document.getElementById("results").appendChild(elem);
    
     elem.onclick();
     assert( Button.clicked, "The clicked property was correctly set on the object" ); 
    Add a method to all functions to allow context enforcement.
     Function.prototype.bind = function(object){ 
       var fn = this;
       return function(){ 
         return fn.apply(object, arguments);
       }; 
     }; 
    
     var Button = { 
       click: function(){ 
         this.clicked = true; 
       } 
     }; 
      
     var elem = document.createElement("li");
     elem.innerHTML = "Click me!";
     elem.onclick = Button.click.bind(Button);
     document.getElementById("results").appendChild(elem);
    
     elem.onclick();
     assert( Button.clicked, "The clicked property was correctly set on the object" ); 
    Our final target (the .bind method from Prototype.js).
     Function.prototype.bind = function(){ 
       var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift(); 
       return function(){ 
         return fn.apply(object, 
           args.concat(Array.prototype.slice.call(arguments))); 
       }; 
     }; 
    
     var Button = { 
       click: function(value){ 
         this.clicked = value; 
       } 
     }; 
      
     var elem = document.createElement("li");
     elem.innerHTML = "Click me!";
     elem.onclick = Button.click.bind(Button, false);
     document.getElementById("results").appendChild(elem);
    
     elem.onclick();
     assert( Button.clicked === false, "The clicked property was correctly set on the object" ); 
    Bonus: Function Length
    How does a function's length property work?
     function makeNinja(name){} 
     function makeSamurai(name, rank){} 
     assert( makeNinja.length == 1, "Only expecting a single argument" ); 
     assert( makeSamurai.length == 2, "Multiple arguments expected" ); 
    We can use it to implement method overloading.
     function addMethod(object, name, fn){ 
       // Save a reference to the old method
       var old = object[ name ]; 
    
       // Overwrite the method with our new one
       object[ name ] = function(){ 
         // Check the number of incoming arguments,
         // compared to our overloaded function
         if ( fn.length == arguments.length ) 
           // If there was a match, run the function
           return fn.apply( this, arguments );
    
         // Otherwise, fallback to the old method
         else if ( typeof old === "function" ) 
           return old.apply( this, arguments ); 
       }; 
     } 
    How method overloading might work, using the function length property.
     function addMethod(object, name, fn){ 
       // Save a reference to the old method
       var old = object[ name ]; 
    
       // Overwrite the method with our new one
       object[ name ] = function(){ 
         // Check the number of incoming arguments,
         // compared to our overloaded function
         if ( fn.length == arguments.length ) 
           // If there was a match, run the function
           return fn.apply( this, arguments );
    
         // Otherwise, fallback to the old method
         else if ( typeof old === "function" ) 
           return old.apply( this, arguments ); 
       }; 
     } 
    
     function Ninjas(){ 
       var ninjas = [ "Dean Edwards", "Sam Stephenson", "Alex Russell" ]; 
       addMethod(this, "find", function(){ 
         return ninjas; 
       }); 
       addMethod(this, "find", function(name){ 
         var ret = []; 
         for ( var i = 0; i < ninjas.length; i++ ) 
           if ( ninjas[i].indexOf(name) == 0 ) 
             ret.push( ninjas[i] ); 
         return ret; 
       }); 
       addMethod(this, "find", function(first, last){ 
         var ret = []; 
         for ( var i = 0; i < ninjas.length; i++ ) 
           if ( ninjas[i] == (first + " " + last) ) 
             ret.push( ninjas[i] ); 
         return ret; 
       }); 
     } 
      
     var ninjas = new Ninjas(); 
     assert( ninjas.find().length == 3, "Finds all ninjas" ); 
     assert( ninjas.find("Sam").length == 1, "Finds ninjas by first name" ); 
     assert( ninjas.find("Dean", "Edwards").length == 1, "Finds ninjas by first and last name" ); 
     assert( ninjas.find("Alex", "X", "Russell") == null, "Does nothing" );