confirm('Can you read this from where you are sitting?')

A Look at Native OO Using Prototype

(No, not the framework.)

Titus Stone

Emailtitus@theblogfrog.com
Twitter@andstuff
Githubhttps://github.com/tstone

Token Definition First Slide

prototype

the original or model on which something is based or formed

Creating New Types

Defining a new (blank) type

var myType = function(){};

Creating an instance of that type

var a = new myType();

What does this all mean?

typeof myType
// => "function"
typeof a
// => "object"

Types/functions have a special property to assign members

var myType = function(){};

// Members added to the prototype, just like assigning
// keys to a hash
myType.prototype.value = 2000;
myType.prototype['name'] = 'Jim';

Members on the prototype become "available" on the instance

var myType = function(){};
myType.prototype.value = 2000;
myType.prototype['name'] = 'Jim';

var x = new myType();
console.log(x.name);
// => "Jim"

Nowhere was the key/member "name" assigned to the instance "x":

var x = new myType();
// Didn't happen:
x.name = 'Jim';

This works because JS recursivley searches for members

console.log(x.name);

Every type has a prototype.

typeof Object.prototype
// => "object"

JS automatically assigns a prototype, even if the code doesn't specify

var typeA = function(){};
var typeB = function(){};
typeB.prototype = new Object();

typeof typeA.prototype === typeof typeB.prototype
// => true

Things to Note:

// Right:
typeB.prototype = new Object();

// Wrong:
typeB.prototype = Object;
// ie. This doens't happen in C#
public class typeB : new Object()
{
    // ...
}

The prototype of a type can be assigned to a user type

var Being = function(){};
Being.prototype.alive = true;

var Human = function(){};
Human.prototype = new Being();
Human.prototype.constructor = Human;  // Ignore for now

var titus = new Human();
titus.alive
// => true

Things to Note:

// Does Human have a member "alive"?
var titus = new Human();
titus.alive
// => true

An instance can be accessed from within itself: this

var Invoice = function(){};

Invoice.prototype.amount = 2000;

Invoice.prototype.pay = function(bank) {
    bank.transfer(this.amount);
}

var i = new Invoice();
i.pay(...);

this is where things get tricky

Rule of Thumb: Every method in JS is an instance method

function debugCurrentUrl() {
    // What is the value of "this"?
    console.log(this.location.href);
}

debugCurrentUrl();
// => "file:///C:/Users/Titus/Desktop/JS%20Pres/presentation.html"

Behind the scenes, JS always assigns a value to "this"

this can be specified

name = 'Bill';

function printName() {
    console.log(this.name);
}

var myType = function(){};
myType.prototype.name = 'Jen';
myType.prototype.printName = function() {
    console.log(this.name);
}
var a = new myType();

printName();
// => "Bill"

printName.call(a);
a.printName()
// => "Jen"

More Black Magic

In this case, javascript is automatically binding "window" to the value of "this"

// These are equivalents:
printName();
printName.apply(window); // js magic

In this case, we are manually specifying the value of "this" to be "a"

printName.apply(a);

And in this case, javascript is automatically binding "a" to the value of "this" on the prototype's function

// These are also equivalents:
a.printName()
a.prototype.printName.apply(a); // js magic

JS automatically binds the child instance when executing parent methods

var parentType = function(){};
parentType.prototype.name = 'Bill';
parentType.prototype.print = function() {
    console.log(this.name);
};

var childType = function(){};
childType.prototype.name = 'Frank';

var a = new childType();
a.print();
// => "Frank";

Common Trip-ups (#1)

Unlike classic OO, javascript does not automatically bind "this" if the function is passed as a value!

var Widget = function(){};

Widget.prototype.load = function() {
    $('button').click(this.onClick);
}

Widget.prototype.onClick = function() {
    // this === window!!
}

JS would bind "this" correctly if the code were...

$('button').click(this.onClick());

... but that would be wrong

Common Trip-ups (#1) - A Fix

var Widget = function(){};

Widget.prototype.load = function() {
    var that = this;
    $('button').click(function() {
        that.onClick.apply(that, Array.prototype.slice.apply(arguments));
    });
}

Widget.prototype.onClick = function() {
    // this === actual instance
}

This is SUPER UGLY

Common Trip-ups (#1) - A Nicer Fix

var Widget = function(){};

Widget.prototype.load = function() {
    $('button').click(this.onClick.bind(this));
}

Widget.prototype.onClick = function() {
    // this === actual instance
}

Better, but still redundant.

Also, this doesn't work in Safari 4 or IE7 (big surprise).

Common Trip-ups (#2)

var myType = function(){};
myType.prototype.config = {
    vector: 35
};

var a = new myType();
a.config.vector = 25;

var b = new myType();
console.log(b.config.vector);
// => 25;
        

Common Trip-ups (#2) - A fix

var myType = function(){};
myType.prototype.init = function() {
    this.config = {
        vector: 35;
    }
};

var a = new myType();
// This is the cost of this pattern:
a.init();

This works because JS will always bind the correct value of "this" when executing the function

Common Trip-ups (#3) - A Fix

Typing .prototype all the time is fugly

var myType = function(){};
(function(type){

    type.init = function() {
        // ...
    };

    type.property = 'value';

}(myType.prototype));

The self-executing function pattern lets us wrap a nicer keyword for defining prototypes

Common Trip-ups (#3) - A Fix

This pattern also gives us nicer child/parent relationships

var childType = function(){};
(function(type, parent){

    type.init = function() {
        // Code that runs before the parent
        // ...

        // Invoke base method
        parent.init.apply(this);
    };

}(childType.prototype, parentType.prototype));

Is there a better way to do all this?