---
created_at: '2016-12-11T04:47:14.000Z'
title: Learning Advanced JavaScript (2008)
url: http://ejohn.org/apps/learn/
author: kparaju
points: 192
story_text: 
comment_text: 
num_comments: 55
story_id: 
story_title: 
story_url: 
parent_id: 
created_at_i: 1481431634
_tags:
- story
- author_kparaju
- story_13149635
objectID: '13149635'

---
[Source](https://johnresig.com/apps/learn/ "Permalink to Learning Advanced JavaScript")

# Learning Advanced JavaScript

### 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][1] by [John Resig][2].

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" ); 

[1]: http://jsninja.com/
[2]: http://ejohn.org/

