Where are my classes

JavaScript for classical programmers

Classes

Group of methods

Private

Implementation of abstract methods/interfaces

Imitating classes

Where are my...

  • classes
  • instances
  • public/private members
  • public/private methods
  • static methods
  • class inheritance

Empty class


var Book = function() { 

};

Public instance members


var Book = function(isbn, title, author) { 

	this.isbn = isbn; 
	this.title = title || 'No title specified'; 
	this.author = author || 'No author specified';
};

Public instance methods


var Book = function(isbn, title, author) { 

};

Book.prototype.display = function() { };

Public static methods


var Book = function(isbn, title, author) { 

};

Book.convertToTitleCase = function(string) { };

Instances


var Book = function(isbn, title, author) { 
		this.isbn = isbn; 
		this.title = title || 'No title specified'; 
		this.author = author || 'No author specified';
};

Book.prototype.display = function() { };

Book.convertToTitleCase = function(string) { };

var jsPatterns = new Book('9781590599082', 
	'Pro JavaScript Design Patterns', ['Dustin Diaz', 'Ross Harmes']);
jsPatterns.display();

Private members


var Book = function() { 

		var isbn = '9781590599082';
		var title = 'Pro JavaScript Design Patterns';
		var author = ['Dustin Diaz', 'Ross Harmes'];
};

Private methods


var Book = function() { 
	
	var isbn = '9781590599082';

	function checkIsbn(isbn) { 
	    return isbn && isbn.match(/^[\d-]+$/);
	}
};

Public priviledged methods


var Book = function() { 
	
	var isbn = '9781590599082';
	
	function checkIsbn(isbn) { 
	    return isbn && isbn.match(/^[\d-]+$/);
	}
	
	this.setIsbn = function(newIsbn) {
	    isbn = checkIsbn(newIsbn) ? newIsbn : 'No valid isbn specified';
	}; 
};

Two types of public methods combined


var Book = function() { 

	var isbn = '9781590599082';
	var title = 'Pro JavaScript Design Patterns';
	var author = ['Dustin Diaz', 'Ross Harmes'];

	function checkIsbn(isbn) { 
	    return isbn && isbn.match(/^[\d-]+$/);
	}

	this.setIsbn = function(newIsbn) {
	    isbn = checkIsbn(newIsbn) ? newIsbn : 'No valid isbn specified';
	}; 
};

Book.prototype.display = function() { }; 

var jsPatterns = new Book();
jsPatterns.setIsbn('9781590599082');
jsPatterns.display();

Private static members & class constructor


var Book = (function() {
		
		var instances = 0;
		
		// class constructor
		return function() { 
		    instances++;
		}
})();

var book1 = new Book(); // Book -> instances == 1
var book2 = new Book(); // Book -> instances == 2

Private static methods


var Book = (function() {

	// private static method
	function checkIsbn(isbn) { 
	    return isbn && isbn.match(/^[\d-]+$/);
	}
	
	// class constructor
	return function(isbn) { 
	    checkIsbn(isbn)
	}
})();

// public static method
Book.convertToTitleCase = function(string) {}

Static & instance members & methods combined


var Book = (function() {
		
	// private static method
	function checkIsbn(isbn) { 
	    return isbn && isbn.match(/^[\d-]+$/);
	}
	
	return function(isbnCode) { 
	
	    // private member
	    var isbn;
	
	    // private method
	    var isbnToCountry = function() { }
		
	    // public priviledged method
	    this.getCountry = function() { }
		
	    // constructor code
	    isbn = checkIsbn(isbnCode) ? isbnCode : 'No valid isbn specified';

	}
})();

// public static method
Book.convertToTitleCase = function(string) {}

// public non-priviledged instance method
Book.prototype.display = function() { }; 

Classical Inheritance


function Person(name) {
	this.name = name;
}

Person.prototype.getName = function() {
	return this.name;
}

// Author extends Person
function Author(name, books) {
		Person.call(this, name); 
		this.books = books; 
}

Author.prototype = Object.create(Person.prototype); 

var author = new Author('Dustin Diaz', ['JavaScript Design Patterns']);

// accessing a public instance method of the parent class, oh yeah!
author.getName();

Imitating classes
(revisited)

  • So what's wrong with this approach?

Modularization

Code Reuse

Encapsulation

Information Hiding

Abstraction

Classes

Group of methods

Private

Implementation of abstract methods/interfaces

Modularization

Code Reuse

Encapsulation

Information Hiding

Abstraction

Closures

Sending AJAX requests and receiving asynchronouse responses


var sendAjaxRequest = function (url, data) {
    
    var ajaxRequest = new XMLHttpRequest();
    ajaxRequest.open(data.method, url);
    ajaxRequest.setRequestHeader('Content-Type', data.contentType + '; charset=' + data.encoding);
    
    ajaxRequest.onreadystatechange = function() {
        
        if (ajaxRequest.readyState == 4) {
            if (ajaxRequest.status == 200) {
                data.onSuccess(ajaxRequest);
            } else {
                data.onFailure(ajaxRequest);
            }
        }
        
    };
    
    ajaxRequest.send(data.postBody);
};

Not necessarily implemented with an anonymous function


var sendAjaxRequest = function (url, data) {
    
    var ajaxRequest = new XMLHttpRequest();
    ajaxRequest.open(data.method, url);
    ajaxRequest.setRequestHeader('Content-Type', data.contentType + '; charset=' + data.encoding);
    
    var onStateChanged = function() {
        
        if (ajaxRequest.readyState == 4) {
            if (ajaxRequest.status == 200) {
                data.onSuccess(ajaxRequest);
            } else {
                data.onFailure(ajaxRequest);
            }
        }
        
    };
	
    ajaxRequest.onreadystatechange = onStateChanged;
    
    ajaxRequest.send(data.postBody);
};

sendAjaxRequest('/VideoSurveillance/Communication', {
        method: 'POST',
        contentType: 'text/xml',
        encoding: 'utf-8',
        postBody: xml,
        onSuccess: function() {},
        onFailure: function() {}
    });

A simpler example


function makeSizer(size) {
    return function() {
        document.body.style.fontSize = size + 'px';
    };
}

document.querySelector('#size-12').onclick = makeSizer(12);
document.querySelector('#size-14').onclick = makeSizer(14);

In other words, the function defined in the closure 'remembers' the environment in which it was created.

MDN

Self-invoking functions

Let's get back to the Cosmic Encounter Score Board project source code...


var score = {};

games.forEach(function(game) {
    // some calculations
});

// some DOM operations

And encapsulate the globals...


( function() {
						
	var score = {};

	games.forEach(function(game) {
	    // some calculations
	});

	// some DOM operations

}() );

Object literals for pure data


var Settings = {
	CommunicationChanel: '/VideoSurveillance/Communication', 
	VideoChanel: '/VideoSurveillance/Video', 
	ConfigurationAPIChanel: '/Config'
}

{
	"title": "Uploads from everyone",
	"link": "https://www.flickr.com/photos/",
	"description": "",
	"modified": "2014-10-10T15:18:06Z",
	"generator": "https://www.flickr.com/",
	"items": [
	    {
	        "title": "Tenis 29/09",
	        "link": "https://www.flickr.com/photos/ciudaddeldeporte/15309535679/",
	         "media": {"m":"https://farm3.staticflickr.com/2950/15309535679_fe2fc865ca_m.jpg"},
	        "date_taken": "2014-09-29T15:29:25-08:00",
	        "description": " X Olimpiada Escolar",
	        "published": "2014-10-10T15:18:06Z",
	        "author": "nobody@flickr.com (Viņa Ciudad del Deporte)",
	        "author_id": "50826553@N08",
	        "tags": "tenis viņadelmar ciudaddeldeporte xolimpiadaescolar"
	    },
	    {
	        "title": "Prescott-012.jpg",
	        "link": "https://www.flickr.com/photos/9289918@N06/15309535849/",
	        "media": {"m":"https://farm6.staticflickr.com/5597/15309535849_44683b3fc3_m.jpg"},
	        "date_taken": "2014-08-01T10:45:18-08:00",
	        "description": " jlglinnell posted a photo: ",
	        "published": "2014-10-10T15:18:08Z",
	        "author": "nobody@flickr.com (jlglinnell)",
	        "author_id": "9289918@N06",
	        "tags": "prescott vscc"
	    }
	]
}

Creating objects using a constructor


						
var Player = function(name) {

    this.name = name;
    this.ratio = 0;
	
    var gameCount = 0;
    var points = 0;
	
    this.addGame = function(gameDescription) {
        // update points and this.ratio;
    }
}
						

var veso = new Player('veso');

veso.addGame({
        players: 5,
		winners: 1,
		isWinner: false
    });
	
console.log(veso.ratio);
						

At the end of the day, there is almost no difference between an object created with a literal and an object created by a constructor function.

The new keyword is a shortcut for changing the context (using call/apply/bind) to a fresh new object


var veso = new Player('veso');
						

var veso = {};
Player.call(veso, 'veso');
						

Adding methods in the ptototype chain


						
var Player = function(name) {
    // constructor code
}

Player.prototype.display = function() {
    // displays the player on the screen
}
						

var veso = new Player('veso');

veso.addGame({
        players: 5,
		winners: 1,
		isWinner: false
    });
	
veso.display();
						

						
// basic polyfill for Array.prototype.forEach
if (!Array.prototype.forEach) {
    Array.prototype.forEach = function(handler) {
        for (var i = 0; i < this.length; i++) {
            handler(this[i]);
        }
    };
}
						

Creating objects with a prototype

Search in the Web Client also searches for views but uses the Folder visual representation to display them


var folder = new FolderTemplate(data);
// ...
var view = Object.create(folder);
						

"Advanced" singleton using an anonymous constructor

Let's first see how a singleton is defined in JavaScript


var mySingletonObject = {};
						

We've already seen some singletons during the presentation:


var Settings = {
    CommunicationChanel: '/VideoSurveillance/Communication', 
    VideoChanel: '/VideoSurveillance/Video', 
    ConfigurationAPIChanel: '/Config'
}
	

"Advanced" singleton is such an object that embraces encapsulation and does not limit the code to an object literal


var CameraSearchController = new function() {
    var cameraResults = [];
    this.pauseCameras = function() { /* ... */ };
    this.resumeCameras = function() { /* ... */ };
    // some local functions here
}
						

CameraSearchController.pauseCameras();
						

Compare this to first implementing the verboise singleton pattern and then using getInstance() method accross the application


CameraSearchController.getInstance().pauseCameras();
							

Augmenting the this


var CameraThumbnail = function (cameraData) {
    this.start = function() { /* ... */ };
    this.stop = function() { /* ... */ };
}
						

var Camera = function (cameraData) {

    CameraThumbnail.call(this, cameraData);

    this.onLive = function() { /* ... */ };
    this.onPlayback = function() { /* ... */ };
}
						

Conclusion: You don't need to know about the prototype chain to inherit :)

Mixins

Takes the augmenting one step further


var moveMixin = {
    moveUp: function() {
        this.element.style.top = this.position.top - this.speed + 'px';
    },
    moveLeft: function() {
        this.element.style.left = this.position.left - this.speed + 'px';
    }
}
						

var Player = function() { /* ... */ };
var Enemy = function() { /* ... */ };

augment(Player, moveMixin);
						

function augment(dest, source) {
    for (methodName in source.prototype) {
        if (!dest.prototype[methodName]) {
            dest.prototype[methodName] = source.prototype[methodName];
        }
    }
}
						

Objects as namespaces


var milestone = milestone || {};

milestone.connect = function() { /* ... */ };
milestone.logIn = function(uname, password) { /* ... */ };
milestone.disconnect = function() { /* ... */ };
						

var milestone = milestone || {};

milestone.Camera = function(id) { /* ... */ };
milestone.Export = function(id) { /* ... */ };
						

var camera = new milestone.Camera(hash);
camera.start();
						

Duck typing

instead of interface-based polymorphism

"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."

public static void farm(Animal x) {
    System.out.println(x.makeNoise());
}
						

public static void farm(Animal x) {
    System.out.println(x.makeNoise());
}
						

public static void farm(Animal x) {
    System.out.println(x.makeNoise());
}
						

public static void farm(Animal x) {
    System.out.println(x.makeNoise());
}
						

public static void farm(LivingThing x) {
    System.out.println(x.makeNoise());
}
						

public static void farm(LivingThing x) {
    System.out.println(x.makeNoise());
}
						

public static void farm(Noisable x) {
    System.out.println(x.makeNoise());
}
						

If you think the Noisable interface is a joke, please pay attention to the following built-in interfaces in Java:

  • Cloneable
  • Comparable
  • Serializable
  • Runnable

Duck typing in action


var farm = function(x) {
    console.log(x.makeNoise());
}
						

Powerful reflection

instead of complicated dependency injection or reflection APIs

We used to have one type of side panel in the Web Client


// somewhere in the GridEdit component
var addCameraPanel = new SidePanel();
						

But now they are two types - AddCameraPanel and InvestigationsPanel


// somewhere in the GridEdit component, 
// where panelType is provided as a parameter
var addCameraPanel = new window[panelType]();
						

Compare this to the Java Reflection API


try {
    Class myClass = Class.forName("MyClass");
    Class[] types = {Double.TYPE};
    Constructor constructor = myClass.getConstructor(types);
    Object[] parameters = {new Double(0)};
    Object instanceOfMyClass = constructor.newInstance(parameters);
} catch (ClassNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (InstantiationException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (IllegalAccessException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (IllegalArgumentException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (InvocationTargetException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (NoSuchMethodException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (SecurityException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
						

Or C# Reflection API


myObject = (MyAbstractClass)Activator.CreateInstance("AssemblyName", "TypeName");
						

Origin of class-based OOP

  • Aristotle (384-322 BC)

    • essence = genus + differentia
    • Single correct taxonomy of natural things
    • Aristotle finds about the "accidental" properties
  • Criticism - XIX and XX century by different philosophers

    • no universal rules to define a category
    • not a mechanical process but it requires creative invention and evaluation
    • some simple concepts are extremely difficult to define in terms of shared properties - ex: game, work of art
    • no better members of a category
    • categorization depends on the author's background, mental capabilities and experience - ex: snow and ice, colors, etc.
    • ad-hoc categories - what to get for a present, what to get from home in case of a fire, what to do in the weekend

Prototype-based categories

(as defined by the philosophers)
  • graded, fuzzy boundaries, central member with a membership of 1 - ex: red
  • clear boundaries, better members - ex: positive integers

Classes vs. Prototypes

Classes, Types, static languages Prototypes, no types, dynamic languages
better code documentation x
suitable for juniors x
suitable for education x
smaller and coherent code base x
boosts development productivity x
better adapts to changing requirements x

ES6 Classes

Syntax sugar over closures and prototypes


class Vector3 {
 
  constructor (x, y, z) {
    this.x = x; this.y = y; this.z = z;
  }
 
  magnitude() {
    var sqr = p => p * p;
    return Math.sqrt(sqr(this.x) + sqr(this.y) + sqr(this.z));
  }
}
						

var Vector3 = (function () {
 
  function Vector3 (x, y, z) {
    this.x = x; this.y = y; this.z = z;
  }
 
  Vector3.prototype.magnitude = function() {
    var sqr = function(p) { return p * p; };
    return Math.sqrt(sqr(this.x) + sqr(this.y) + sqr(this.z));
  }
; return Vector3;})();
						

Congratulations!

You have now moved beyond global functions as your main tool for building JavaScript applications