The document discusses various design patterns in JavaScript including creational patterns like constructor, prototype, and singleton patterns. It also covers structural patterns such as decorator, facade, and flyweight patterns as well as behavioral patterns like observer and mediator patterns. Examples are provided for how to implement common patterns like module, revealing module, observer, and decorator patterns in JavaScript code.
3. books
• 'Patterns Of Enterprise Application
Architecture' Martin Fowler
• 'JavaScript Patterns' Stoyan Stefanov
• 'Pro JavaScript Design Patterns' Ross Harmes
and Dustin Diaz
• 'Learning JavaScript Design Patterns' Addy
Osmani
• …
4. Categories Of Design Pattern
• Creational Design Patterns
Constructor, Factory, Abstract, Prototype, Singleton and Builder
• Structural Design Patterns
Decorator, Facade, Flyweight, Adapter and Proxy
• Behavioral Design Patterns
Iterator, Mediator, Observer and Visitor
5. Create an object
• var newObject = {};
• var newObject = Object.create(…);
• var newObject = new Class();
http://dmitrysoshnikov.com/ecmascript/javascript-the-core/
7. Prototype pattern
• var anotherCar = Object.create( someCar );
• var beget = (function () {
function F() {};
return function (proto) {
F.prototype = proto;
return new F();
};
})(); Clone using prototype: avoid the inherent cost
8. Singleton pattern
var Singleton = (function () { Anonymous Function
var instantiated;
function init() {
return {
publicMethod: function () {},
publicProperty: 'test'
};
}
return {
getInstance: function () {
if (!instantiated) {
instantiated = init();
}
return instantiated;
}
};
})(); Singleton.getInstance().publicMethod();
9. Module pattern
Loose Augumentation:
var myNamespace = (function () { var MODULE = (function (my) {
var myPrivateVar = 0; // add capabilities...
var myPrivateMethod = function (someText) { return my;
console.log(someText); }(MODULE || {}));
}; Option 1: var myApplication = myApplication || {};
Option 2 if(!MyApplication) MyApplication = {};
return { Option 3: var myApplication = myApplication = myApplication || {}
Option 4: myApplication || (myApplication = {});
myPublicVar: "foo", Option 5: var myApplication = myApplication === undefined ? {} :
myApplication;
myPublicFunction: function (bar) {
myPrivateVar++; Cloning & Inheritance
myPrivateMethod(bar);
for (key in old) {
}
if (old.hasOwnProperty(key)) {
};
my[key] = old[key];
})(); }
}
http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth
10. The Revealing Module Pattern
var myRevealingModule = (function(){
var name = 'John Smith';
function setPerson (personName) {
name = personName;
}
function getPerson () {
Advantage: same style
return name;
} Disadvantage: when over-writting
return {
// set: setPerson,
get: getPerson
};
}());
11. Observer Pattern & Mediator Pattern
var pubsub = {}; q.subscribe = function( topic, func ) { q.unsubscribe = function( token ) {
(function(q) { if (!topics[topic]) { for ( var m in topics ) {
var topics = {}, topics[topic] = []; if ( topics[m] ) {
subUid = -1; } for (var i = 0, j = topics[m].length; i < j; i++) {
q.publish = function( topic, args ) { var token = (++subUid).toString(); if (topics[m][i].token === token) {
topics[topic].push({ topics[m].splice(i, 1);
if ( !topics[topic] ) { token: token, return token;
return false; func: func }
} }); }
var subscribers = topics[topic], return token; }
var tempArr; }; }
return this;
if(subscribers ) { };
tempArr = subscribers .slice(0); }( pubsub ));
while (tempArr .length) {
tempArr.pop().func(topic, args);
}
}
Backbone.js: listen to changes in model
return this;
};
Mediator Pattern: Colleague – communicate by -> Mediator
Observer - listener to -> Mediator
13. Command Pattern
(function(){
var CarManager = {
requestInfo: function( model, id ){},
buyVehicle: function( model, id ){},
arrangeViewing: function( model, id ){}
};
We can keep track of the commands,
})(); also record a game reply
CarManager.execute("arrangeViewing", "Ferrari", "14523");
CarManager.execute("requestInfo", "Ford Escort", "34232");
CarManager.execute("buyVehicle", "Ford Escort", "34232");
CarManager.execute = function (name) {
// we can log the commands here, also implements an undo function
return CarManager[name] && CarManager[name].apply(CarManager, [].slice.call(arguments, 1));
};
14. Façade Pattern
function setStyles(elements, styles) {
var foo = document.getElementById('foo'); for (var i=0, length = elements.length; i < length; i++) {
foo.style.color = 'red'; var element = document.getElementById(elements[i]);
foo.style.width = '150px';
for (var property in styles) {
var bar = document.getElementById('bar'); element.style[property] = styles[property];
}
bar.style.color = 'red';
}
bar.style.width = '150px';
}
var baz = document.getElementById('baz'); //Now you can just write this:
baz.style.color = 'red'; setStyles(['foo', 'bar', 'baz'], {
baz.style.width = '150px'; color: 'red',
width: '150px'
});
Very common in Jquery: .css(), .animate()…
15. Factory Pattern
var AbstractVehicleFactory = (function () {
var types = {};
return {
getVehicle: function (type, customizations) {
// we can keep track of vehicle or get vehicle by options instead of type …
var Vehicle = types[type];
return (Vehicle) ? return new Vehicle(customizations) : null;
},
registerVehicle: function (type, Vehicle) {
var proto = Vehicle.prototype;
if (proto.drive && proto.breakDown) {
types[type] = Vehicle;
}
return AbstractVehicleFactory;
}
};
})();
AbstractVehicleFactory.registerVehicle("car", Car); Give me a android phone with
AbstractVehicleFactory.registerVehicle("truck", Truck); 4.1 inch screen around S$400
var car = AbstractVehicleFactory.getVehicle("car", { color: "yellow", turbo: true });
var truck = AbstractVehicleFactory.getVehicle("truck", { monster: true, cylinders: 12 });
16. Mixin Pattern
function augment( receivingClass, givingClass ) { var Car = function( settings ){
if ( arguments[2] ) { this.model = settings.model ;
for (var i=2, len=arguments.length; i<len; i++) { this.colour = settings.colour ;
receivingClass.prototype[arguments[i]] = };
givingClass.prototype[arguments[i]];
} var Mixin = function(){};
}
Mixin.prototype = {
else {
driveForward: function(){},
for ( var methodName in givingClass.prototype ) {
if ( !receivingClass.prototype[methodName] ) {
driveBackward: function(){}
receivingClass.prototype[methodName] = };
givingClass.prototype[methodName];
} augment( Car, Mixin,'driveForward','driveBackward' );
}
} var vehicle = new Car({model:'Ford', colour:'blue'});
}
vehicle.driveForward();
vehicle.driveBackward();
We have update our factory to produce Class level
tanks with new weapon
18. • Simple decorator • Mulit decorator
function Vehicle( vehicleType ){
function MacBook() {
this.vehicleType = vehicleType || 'car', this.cost = function () { return 997; };
this.model = 'default', this.screenSize = function () { return 13.3; };
this.license = '00000-000' }
} function Memory( macbook ) {
function makeTruck(truck){ var v = macbook.cost();
truck.setModel =function( modelName ){} macbook.cost = function() { return v + 75 ;}
}
truck.setColor = function( color ){} function LargeScreen( macbook ){
return truck; var v = macbook.cost();
} macbook.cost = function(){ return v + 100; };
macbook.screenSize=function(){ return 14.8; };
var truck = makeTruck(new Vehicle()); }
function Insurance( macbook ){
var v = macbook.cost();
macbook.cost = function(){ return v + 250; };
}
var mb = new MacBook();
Memory(mb);
LargeScreen(mb);
Insurance(mb);
user can wear equips to change attr
and gain new ablitities
19. Pseudo-classical decorators: interface
var TodoList = new Interface('Composite', ['add', 'remove']);
var TodoItem = new Interface('TodoItem', ['save']);
var myTodoList = function(id, method, action) {
// implements TodoList, TodoItem
};
Make sure that only archers
function addTodo( todoInstance ) { can enter certain stage
Interface.ensureImplements(todoInstance, TodoList, TodoItem);
// …
}
https://gist.github.com/1057989
var Interface = function(name, methods),…-
Interface.ensureImplements = function(obj),…-
20. Flyweight pattern
var Book = function(){ Book info:
this.id = id; var Book = function () {
this.title = title; this.title = title;
this.author = author; this.author = author;
this.genre = genre; this.genre = genre;
this.pageCount = pageCount; this.pageCount = pageCount;
this.publisherID = publisherID; this.publisherID = publisherID;
this.ISBN = ISBN; this.ISBN = ISBN;
this.checkoutDate = checkoutDate; };
this.checkoutMember = checkoutMember;
this.dueReturnDate = dueReturnDate;
this.availability = availability; Checkout Info:
};
Share date as much as possible
21. var BookRecordManager = (function () {
var BookFactory = (function () { var bookRecordDatabase = {};
var existingBooks = {}; return {
return {
createBook: function () { addBookRecord: function (
var existingBook = existingBooks[ISBN]; var book = bookFactory.createBook();
if (existingBook) { bookRecordDatabase[id] = {
return existingBook; checkoutMember: checkoutMember,
} else { checkoutDate: checkoutDate,
var book = new Book(); dueReturnDate: dueReturnDate,
existingBooks[ISBN] = book; availability: availability,
return book; book: book
} };
} },
}
}); updateCheckoutStatus: function () {},
extendCheckoutPeriod: function () {},
isPastDue: function (bookID) {}
};
});
22. Object Pool
Pool = {arr:[]};
Pool.pop = function(){
if(Pool.arr.length > 0){
return Pool.arr.pop().wake();
}else{
return new Obj();
}
} May have hundreds of planes through the
Pool.push= function(obj){ whole game, but not more than 20 at
the same time.
Pool.arr.push(obj);
obj.sleep(); Generator a plan: pop()
} Leave screen or destroyed: push()
Object pools are used to avoid the instantiation
cost of creating new objects by re-using existing
ones. Avoid creating objects is gc-friendly. Reuse resource when it has a high cost to
make or release, eg. connection pool
23. MV* pattern
• V: Templing
Handlebars, Underscore…
• *: bind event, route, render, data-binding…
• C: flex, extendable, suit for big project
Backbone, Spine…
• P: Present Model -> View, view no direct access to model,
safer then MVC
Backbone , Angular…
• VM: VM & V more tightly, suit small project
Knockout…
TODO MVC http://todomvc.com/
24. Namespace
• Automating nested namespacing
// automatic namespacing
var myApp = myApp || {};
var application = {
utilities:{ function extend( ns, ns_string ) {
drawing:{ var parts = ns_string.split('.'),
canvas:{ parent = ns,
2d:{} pl, i;
} pl = parts.length;
} for (i = 0; i < pl; i++) {
} if (typeof parent[parts[i]] == 'undefined') {
}; parent[parts[i]] = {};
}
parent = parent[parts[i]];
}
return parent;
}
var mod = extend(myApp, 'myApp.modules.module2');
25. • Deep object extension
function extend(destination, source) {
var toString = Object.prototype.toString,
objTest = toString.call({});
for (var property in source) {
if (source[property] && objTest == toString.call(source[property])) {
destination[property] = destination[property] || {};
extend(destination[property], source[property]);
} else {
destination[property] = source[property];
}
}
return destination;
};
Jquery.extend
26. Design pattern in JQuery core
• Adapter pattern
.css({opacity:.5})
• Façade pattern
.ajax()
• Observe pattern
.on(event, cssSelector, func)
• Iterator pattern
array is also object
• Proxy pattern
.proxy(function(),this…-, thisObj) control before sth really
loaded
• Builder Pattern
$(“<a>a link</a>”).appendTo(…) xml initing
29. CMD
• require()
• Exports http://wiki.commonjs.org/wiki/Modules/Wrappings
cmd: Common Module Definition
Sea.js define(function(require, exports, module) { // The module code goes here });
• Modules are singletons.
• New free variables within the module scope should not be introduced.
• Execution must be lazy, import can be later: define(dependency, …) vs.
define(function(require(…))
• With some care, modules may have cyclic dependencies.