Classes
Group of methods
Private
Implementation of abstract methods/interfaces
var Book = function() {
};
var Book = function(isbn, title, author) {
this.isbn = isbn;
this.title = title || 'No title specified';
this.author = author || 'No author specified';
};
var Book = function(isbn, title, author) {
};
Book.prototype.display = function() { };
var Book = function(isbn, title, author) {
};
Book.convertToTitleCase = function(string) { };
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();
var Book = function() {
var isbn = '9781590599082';
var title = 'Pro JavaScript Design Patterns';
var author = ['Dustin Diaz', 'Ross Harmes'];
};
var Book = function() {
var isbn = '9781590599082';
function checkIsbn(isbn) {
return isbn && isbn.match(/^[\d-]+$/);
}
};
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';
};
};
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();
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
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) {}
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() { };
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();
Modularization
Code Reuse
Encapsulation
Information Hiding
Abstraction
Classes
Group of methods
Private
Implementation of abstract methods/interfaces
Modularization
Code Reuse
Encapsulation
Information Hiding
Abstraction
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.
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
}() );
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"
}
]
}
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');
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]);
}
};
}
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);
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();
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 :)
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];
}
}
}
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();
"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:
Duck typing in action
var farm = function(x) {
console.log(x.makeNoise());
}
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");
Aristotle (384-322 BC)
Criticism - XIX and XX century by different philosophers
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 |
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;})();