<!DOCTYPE html>
<!--
        Javascript prototypes explained.
-->
<html>
<head>
        <meta charset="utf-8">
</head>

<body>
</body>

<script>

// some utility functions...
function showKeys(prefix, obj) {
        if( typeof obj === 'undefined' ) {
                document.write("<p>" + prefix + ": undefined</p>");
        }
        else {
                document.write("<p><b>" + prefix + " Type:</b> " +
                                        typeof obj + "</p>");
                var json = JSON.stringify(Object.keys(obj));
                if( json !== undefined ) {
                        document.write("<p><b>" + prefix + " Keys:</b> " +
                                json + "</p>");
                }
        }
}

function showOwn(name, obj) {
        document.write("<h2>" + name + "</h2>");
        showKeys("Object", obj);
        showKeys("__proto__", obj.__proto__);
        showKeys("prototype", obj.prototype);
        document.write("<hr>");
}

function step(s) {
        document.write("<h1>" + s + "</h1>");
}

function print(msg) {
        document.write("<p><b>Print:</b> " + msg + "</p>");
}

function error(msg) {
        document.write("<p><b>Assert Failed:</b> " + msg + "</p>");
}

function assert(condition, msg) {
        if( !condition )
                error(msg);
}



/////////////////////////////////////////////////////////////////////////////
//
// Javascript prototype inheritance explained in 10 easy steps.
//
// Run this in a browser, and follow the output with the comments.
//

//
// 1) Everything is an object.  MyObject is an object with a type of function.
//      MyObject itself contains no properties.  But it is a function,
//      which creates a property 'hello' when it runs.
//
step("1.");
function MyObject() {
        this.hello = function() {
                print("hello #1");
        }
        this.this_is_new_MyObject = "";
}
showOwn("MyObject", MyObject);

//
// 2) Every object has an internal [[Prototype]] property, which some
//      JS implementations allow access to via __proto__.  This property
//      controls the prototype chain.  If you access a property in the object
//      which doesn't exist, JS will check __proto__, and then
//      __proto__.__proto__, and so on up the chain until the end.
//
//      This is why there is a difference between hasOwnProperty() and
//      the in operator.  'in' looks in the prototype chain.
//
step("2.");
assert(MyObject.__proto__ === Function.prototype, "MyObject derives from Function");

//
// 3) Every object can have an optional .prototype property.  This is
//      NOT part of the prototype chain, but simply determines the
//      value of a new object's __proto__.
//
//      Side notes:
//              Since MyObject is a function, it's __proto__ comes from
//              Function.prototype.
//
//              MyObject.prototype is an empty Object created automatically,
//              but we can override this later.
//
step("3.");
showOwn("MyObject.prototype", MyObject.prototype);

//
// 4) We can override our earlier prototype with a new object.
//
step("4.");
function Base() {
        this.hello = function() {
                print("Base hello");
        }

        this.goodbye = function() {
                print("Base goodbye");
        }

        this.this_is_new_Base = "";
}

var base = new Base();
MyObject.prototype = base;
var o = new MyObject();
showOwn("MyObject", MyObject);
showOwn("base", base);
showOwn("o", o);

//
// 5) We now have:
//      o               A new object, with a property .hello referencing hello1,
//                      since the MyObject constructor function set it up.
//      o.__proto__     This points to the new Base() we created, taken from
//                      MyObject.prototype.
//      o.prototype     undefined
//
step("5.");
o.hello();      // hello1
o.goodbye();    // there is no goodbye property in the object, so uses
                // o.__proto__.goodbye()
assert(o.goodbye === o.__proto__.goodbye, "goodbye comes from prototype");
assert(o.prototype === undefined, "o.prototype is undefined");

//
// 6) Changing the base object also changes the behaviour of any object
//      descending from it!  In other words, __proto__  objects are
//      SHARED across all objects that have the same object reference.
//
step("6.");
base.goodbye = function() {
        print("Base was changed, goodbye!");
}
o.goodbye();    // o.__proto__ still points to base, which has now been
                // changed, so prints new string

showOwn("base", base);
showOwn("o", o);

//
// 7) But replacing the entire object at the .prototype level does not
//      change the prototype chain, since that is based on the hidden
//      __proto__.  You can change the object itself *through* the
//      prototype reference, but replacing it loses the reference.
//
step("7.");
// adding a wave() member to base through the MyObject.prototype reference
MyObject.prototype.wave = function() {
        print("No talking, just waving");
}
o.wave();
assert(o.__proto__ === base, "base is still the prototype object");
assert(base === MyObject.prototype, "even through MyObject");
base.wave();            // new function created through MyObject.prototype ref!

var empty_proto = new Object();
MyObject.prototype = empty_proto;       // but resetting to something empty
o.goodbye();                            // does not change o.__proto__
o.wave();                               // which still references base...
                                        // these functions still exist via
                                        // __proto__

showOwn("MyObject", MyObject);
showOwn("base", base);
showOwn("o", o);

//
// 8) But new objects have their __proto__ setup with the new prototype.
//
step("8.");
var p = new MyObject();
assert(p.__proto__ === empty_proto, "assigned from the new .prototype");
assert(p.goodbye === undefined, "p.goodbye is undefined");
assert(p.hello !== o.hello, "these hello functions are unique, created twice by the MyObject constructor, doing the same thing");
p.hello();
o.hello();
showOwn("p", p);

//
// 9) Remember... EVERYTHING is an object, and therefore you can
//      inherit from it.
//
step("9.");

// Create dummy function
function F() {
        this.this_is_new_F = "";
}
F.prototype = o;

// New object uses o as its __proto__
var q = new F();
showOwn("q, fresh", q);
assert(q.__proto__ === o, "q derives from o");
assert(q.__proto__.hello === o.hello, "q.o.hello done explicitly");
assert(q.__proto__.__proto__.hello === base.hello, "q.o.base.hello done explicitly");
q.hello();      // q.__proto__.hello() comes first in the chain
assert(q.__proto__.__proto__.goodbye === base.goodbye, "the only goodbye in the chain... we can't test this explicitly since q.goodbye follows the chain automatically");
q.goodbye();    // still changed! (q.__proto__.__proto__.goodbye())

// q can hide its prototype
q.hello = function() {
        print("q.hello here");
}
q.hello();              // q's version
q.__proto__.hello();    // but the prototype still exists: hello1
q.__proto__.__proto__.hello(); // as well as the base class

showOwn("q, with hide", q);

//
// 10) Use Object.create() to create derived objects more easily, instead
//      of the manual method above.
//
step("10.");
var r = Object.create(q);
r.hello();                      // q.hello from the prototype
r.__proto__.__proto__.hello();  // hello1
showOwn("r", r);

//
// 11) If you use the var self = this; tactic, note that you will not be
//      able to override base class member functions like you could if you
//      used this.
//
step("11.");
function ThisBase() {
        this.work = function() {
                print("ThisBase.work");
        }

        this.hello = function() {
                print("ThisBase.hello");
                this.work();
        };
}
function SelfBase() {
        var self = this;

        self.work = function() {
                print("SelfBase.work");
        };

        self.hello = function() {
                print("SelfBase.hello");
                self.work();    // self never changes, regardless of
                                // derived class, since it's a closure variable
        };
}

function D(){
        this.work = function() {
                print("D.work");
        };
}
D.prototype = new ThisBase();
var d1 = new D();
D.prototype = new SelfBase();
var d2 = new D();

d1.hello();     // work() overridden
d2.hello();     // work() not overridden

//
// Conclusion: constructors create fresh instances of their members and
//      functions each time they are called, but __proto__ simply contains
//      a reference to the prototype object, copied from .prototype, which is
//      the same object for all objects that reference it via their internal
//      __proto__ variables; and these __proto__ variables never change,
//      although .prototype can change, which only affects the next
//      object created with new.
//

</script>

</html>