You are on page 1of 62

An Introduction

Basic Topics
Namespaces
Classes
Encapsulation
Inheritance
Interfaces
Advanced Topics
Singleton Pattern
Static
Attributes/Methods
Solution
Encapsulation
JavaScript Build
Overview
JavaScript does not support many of the typical object oriented features
found in other languages. However JavaScript is powerful enough to create
these features on top of the language.

Here is a list of types JavaScript supports:
var a = true; //boolean
var b = 1; //numeric
var c = 'Ben'; //string
var d = []; //arrays
var e = {}; //objects
var f = function () { ... }; //functions

Introduction
Namespaces in JavaScript are simply a collection of nested objects that hold
onto references to class constructors.
//we start by declaring the root namespace
var ninemsn = window.ninemsn || {};

//then we can declare sub namespaces
ninemsn.portal = ninemsn.portal || {};
ninemsn.portal.thirdparty = ninemsn.portal.thirdparty || {};

//add some classes to the namespace
ninemsn.portal.thirdparty.Api = function() { ... };
ninemsn.portal.thirdparty.Config = function() { ... };

Namespaces
Be defensive, always check to see if a namespace already exists. Here is why:
//somewhere at the top of the page
ninemsn.portal.ads = ninemsn.portal.ads || {}
...
//a bit further down
ninemsn.portal.omniture = ninemsn.portal.omniture || {};
...
//a bit further down
ninemsn.portal.video = ninemsn.portal.video || {};

//and even further down some code which clears the ninemsn namespace
var ninemsn = {}; //never do this!

Namespaces
Avoid using the global namespace. If you need some temporary work space
use self executing functions:
//instead of this
var today = new Date();
var friendlyDate = today.getDate() + '/'
+ today.getMonth() + '/' + today.getgetFullYear();
$('#year').html(friendlyDate);

//do this
(function() {
var today = new Date();
var friendlyDate = today.getDate() + '/'
+ today.getMonth() + '/' + today.getgetFullYear();
$('#year').html(friendlyDate);
}());

Namespaces

The Global namespace:

Treat it like a public toilet. You can't avoid it, but while
you're there have the least contact possible: you have no
idea who has been here, or what they've done, and no idea
who and what will come after.

- Dmitry Baranovskiy
Namespaces
To create a class we build a class constructor function. When this function is
called it returns a new instance of the class.

How this constructor function constructs the instance can vary. There are
two main methods:
Creating instances dynamically
Creating instances using the Prototype

What's the difference between the two?
Classes
Classes
Instance 1
Method 1
Method 2
Method 3
Attribute 1
Attribute 2
Instance 2
Method 1
Method 2
Method 3
Attribute 1
Attribute 2
Instance 3
Method 1
Method 2
Method 3
Attribute 1
Attribute 2
Instance 4
Method 1
Method 2
Method 3
Attribute 1
Attribute 2
Dynamic Class Creation
Classes
Prototype
Method 1
Method 2
Method 3
Instance 1
Attribute 1
Attribute 2
Instance 4
Attribute 1
Attribute 2
Instance 3
Attribute 1
Attribute 2
Instance 2
Attribute 1
Attribute 2
Prototype Class Creation
Allows for encapsulation
Inheritance behaviour is
similar to most server
side languages
Is simple to implement
Does not perform as well
as the prototype method
due to additional
memory consumption
Breaks the instanceof
method when using
inheritance (however the
use of interfaces makes
this less of an issue)
P
r
o
s

C
o
n
s

Classes
Dynamic Class Creation
Classes
Prototype Class Creation
Performance
Does not break the instanceof
method when using
inheritance
Does not support
encapsulation
Inheritance behaviour is
unlike most server side
languages and will be
unfamiliar to most
Is complex and unintuitive to
implement (the use of a
helper function to wrap up
this complexity makes this
less of an issue).
P
r
o
s

C
o
n
s

Classes
1 10 100 1000 10000 100000 1000000
Prototype 0 0 0 1 7 60 650
Dynamic 0 0 1 2 34 258 3592
0
500
1000
1500
2000
2500
3000
3500
4000
Execution Time (ms)
Classes
1 10 100 1000 10000 100000 1000000
Prototype 1.64 1.64 1.64 1.7 2.32 8.9 75.15
Dynamic 1.63 1.64 1.69 2.12 6.48 50.41 527.22
0
100
200
300
400
500
600
Memory Consumption (MB)
var Man = function () {
this.changeMood = function(mood) { ... };
this.speak = function (text) { ... };
this.wave = function () { ... };
this.wink = function () { ... };
};

var ben = new Man();
ben.speak('Hello');
ben.wave();
Classes
var Man = function (location) {
//public methods
this.changeMood = function(mood) { ... };
this.speak = function (text) { ... };
this.wave = function () { ... };
this.wink = function () { ... };

//instance constructor
$(location).html(...); //render the man
};

var ben = new Man('#world');
ben.speak('Hello');
ben.wave();
Classes
Encapsulation is achieved using a feature of JavaScript called closures.

What is a closure?
A closure is the local variables for a function - kept alive after the function
has returned
A closure is a stack-frame which is not de-allocated when the function
returns (as if a 'stack-frame' were malloc'ed instead of being on the
stack!).
Encapsulation
An example of a closure:

function sayHello(name) {
var text = 'Hello ' + name; // local variable
return function() { alert(text); };
}

var say = sayHello('Ben');
say(); //Hello Ben

Encapsulation
var Man = function (location) {
//private constants
var _MARKUP = '{html markup goes here}';

//private attributes
var _root;

//public methods
this.changeMood = function(mood) { ... };
...
//instance constructor
_root = $(location);
_root.html(_MARKUP); //render the man
};
Encapsulation
Encapsulation
Inheritance is implemented by a child class calling the constructor of its
parent. The scope of the child is passed to the parent.
var Parent = function() {
this.methodA = function() { ... };
this.methodB = function() { ... };
};

var Child = function() {
Parent.call(this); //pass the parent the scope of the child
this.methodC = function() { ... };
};

var instance = new Child();
instance.methodA();

Inheritance
var Parent = function() {
this.methodA = function() { ... };
};

var Child = function() {
Parent.call(this);
var _base = { methodA: this.methodA };
this.methodA = function() {
_base.methodA();
//do additional stuff
};
this.methodB = function() { ... }
};
Inheritance
Inheritance
Interfaces are implemented using a concept called Duck Typing.

Duck Typing:
When I see a bird that walks like a duck and swims like a duck and quacks like a
duck, I call that bird a duck.
- James Whitcomb Riley

An interface is a list of function names. An object implements the interface if
it has all the functions in the list.

A helper class is used to centralise the checking.
Interfaces
//declare the interface
var IPerson = new Interface('IPerson',
['changeMood', 'speak', 'wave', 'wink']);

//declare the class
var Man = function() { ... }; //implements IPerson

//create the instance
var ben = new Man();

//ensure the object implements the interface
Interface.ensureImplements(ben, IPerson);
Interfaces
Interfaces
Is useful when you only want to guarantee only one instance of your class
exists, e.g. API or Configuration objects.

Works by hiding the constructor function in a closure and never exposing it
to the rest of the page.

Can be implemented in two ways:
1. Automatic loading
2. Lazy loading

CAVEAT: This pattern breaks inheritance.
Singleton Pattern
Automatic Loading

var PersonFactory = function () {

function constructor() {
this.getPerson = function (location) { ... }
};

return new constructor();
} ();

var factory = new PersonFactory(); //TypeError: PersonFactory is not a constructor
var person1 = PersonFactory.getPerson('#world');

Singleton Pattern
Lazy Loading
var PersonFactory = function () {
var _instance;
function constructor() {
this.getPerson = function (location) { ... }
};
return {
getInstance: function () {
return _instance || (_instance = new constructor());
}
};
} ();

var person1 = PersonFactory.getInstance().getPerson('#world');
Singleton Pattern
The Singleton Pattern
Not all attributes and methods need to be attached to every single instance.
We can improve performance by making these things static.

Some common situations where this would be applicable:
You have a method that does not need access to the instance
You have a method which does need access to the instance, but the value
needed can be passed in as a parameter
You have an attribute which is constant

Static attributes and methods are implemented by wrapping the constructor
with a function in order to create a closure where the static items can live.
Static Attributes/Methods
var Class = function () {
var _PRIVATE_CONSTANT_VALUE = '{something important}';

function privateStaticMethod() {
//something useful
};

function constructor() {
this.publicMethod = function () {
privateStaticMethod();
...
};
};

return constructor;
} ();
Static Attributes/Methods
var Class = function () {
function constructor() {
this.publicMethod = function () {
constructor.publicStaticMethod();
...
};
};

constructor.publicStaticMethod = function() { ... };

return constructor;
} ();

Class.publicStaticMethod();
Static Attributes/Methods
var Class = function () {
var _instanceCount = 0;

function constructor() {
this.publicMethod = function () { ... };
_instanceCount++;
};

constructor.getInstanceCount = function() {
return _instanceCount;
};

return constructor;
} ();
Static Attributes/Methods
var instances = [];
for (var i = 0; i < 10; i++) {
instances[i] = new Class();
}

Class.getInstanceCount(); //returns 10

Static Attributes/Methods
Static Attributes/Methods
Encapsulation at a class level is a great way of hiding the inner workings of a
class, but sometimes we want to hide the inner workings of a solution.

Usually only a small number of classes need to be accessible, the rest can
remain hidden in a closure.

This forces all users of a solution to interact in the intended way.

Refactoring is greatly simplified as there is now a guarantee that no external
code is dependant on the internal solution classes.
Solution Encapsulation
(function (demos) {
var IPerson = new Interface(...);
var MoodType = { ... };
var Person = function () { ... };
var Man = function () { ... };
var Woman = function () { ... };
var PersonFactory = function () { ... };

//expose public objects
demos.demo6.MoodType = MoodType;
demos.demo6.PersonFactory = PersonFactory;

} (demos));

Solution Encapsulation
Solution Encapsulation
Keeping each class in its own file makes big solutions easy to maintain, but
we dont want to include each file individually on the page.

We can use MS Build to combine all files in a solution into a single file for use
on the page.

We can also use MS Build to minify this single combined file to reduce the
file size.
JavaScript Build
Add version to the file name
Minify the combined file into a minified file
Merge the individual files into a combined file
Declare the list of files to be minified
Clear any readonly flags on the output files
Declare variables (such as the path to the minified file, solution version etc)
JavaScript Build
MS Build is an XML file containing instructions telling the builder what to do.
The JavaScript build is broken down into the following steps:

Declare variables (such as the path to the minified file, solution version etc):

<PropertyGroup>
<Project>demo7</Project>
<Version>1.0.0</Version>
<VersionFile>$(SourceLocation)js\demo7\code\version.js</VersionFile>
<MergedFile>$(SourceLocation)js\demo7\$(Project)-latest.js</MergedFile>
<CompressedFile>$(SourceLocation)js\demo7\$(Project)-
latest.min.js</CompressedFile>
</PropertyGroup>
JavaScript Build
Clear any read-only flags on the output files:

<Exec Command="attrib -r &quot;$(MergedFile)&quot;" />
<Exec Command="attrib -r &quot;$(CompressedFile)&quot;" />

JavaScript Build
Declare the list of files to be minified:

<ItemGroup>
<Files Include="$(SourceLocation)js\demo7\code\IPerson.js" />
<Files Include="$(SourceLocation)js\demo7\code\MoodType.js" />
<Files Include="$(SourceLocation)js\demo7\code\Person.js" />
<Files Include="$(SourceLocation)js\demo7\code\Man.js" />
<Files Include="$(SourceLocation)js\demo7\code\Woman.js" />
<Files Include="$(SourceLocation)js\demo7\code\PersonFactory.js" />
</ItemGroup>

JavaScript Build
Merge the individual files into a combined file:

<MergeFiles
InputFiles="@(Files)"
OutputFile="$(MergedFile)"
Project="$(Project)"
Version="$(Version)"
VersionFile="$(VersionFile)" />

JavaScript Build
Minify the combined file into a minified file:

<CompressorTask
JavaScriptFiles="$(MergedFile)"
ObfuscateJavaScript="True"
PreserveAllSemicolons="True"
DisableOptimizations="False"
EncodingType="Default"
DeleteJavaScriptFiles="False"
LineBreakPosition="-1"
JavaScriptOutputFile="$(CompressedFile)"
LoggingType="ALittleBit"
ThreadCulture="en-au"
IsEvalIgnored="False" />

JavaScript Build
Add version to the file name:
<Copy
SourceFiles=
"$(MergedFile);$(CompressedFile)"
DestinationFiles=
"$(MergedFileWithVersion);$(CompressedFileWithVersion)" />

JavaScript Build
We can also use MS Build to help with Solution Encapsulation.

Solution Encapsulation is essentially a wrapper around the entire solution.

We can build the solution as individual files and merge them together.
Consider this single file to be our solution.

We can then create a file which is the wrapper. This wrapper encapsulates
the solution, so we need to take the solution file and place it into this
wrapper.

The wrapper defines a placeholder so MS Build knows where to place the
solution.
JavaScript Build
Howeverwhat about the namespaces?

The individual classes attach themselves to the public namespace. By doing
this our solution encapsulation is bypassed.

This can be fixed by the wrapper. It can declare two namespaces, a public
one visible to everyone, and a private one visible to only classes within the
solution.

All classes attach themselves to the private namespace. The wrapper then
selectively exposes the classes which should be public.
JavaScript Build
(function () {

var demosExternal = window.demos;
var demos = {};
demos.demo7 = {};

{library}

//expose public objects
demosExternal.demo7.MoodType = demos.demo7.MoodType;
demosExternal.demo7.PersonFactory = demos.demo7.PersonFactory;
} ());

JavaScript Build
JavaScript Build
Basic Topics
Namespaces
Classes
Encapsulation
Inheritance
Interfaces
Advanced Topics
Singleton Pattern
Static
Attributes/Methods
Solution
Encapsulation
JavaScript Build
Summary

You might also like