├── .gitignore ├── README.md ├── command.js ├── constructor.js ├── decorator.js ├── facade.js ├── factory.js ├── flyweight.js ├── iterator.js ├── mediator.js ├── memento.js ├── mixins.js ├── module.js ├── mvc.js ├── observer.js ├── prototype.js ├── proxy.js ├── singleton.js └── visitor.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # javascript design patterns cheat sheet using jquery for some examples. 2 | Simple examples to help you understand Design Patterns in Javascript 3 | 4 | __Available__ 5 | 6 | * [command](./command.js) 7 | 8 | * [constructor](./constructor.js) 9 | 10 | * [decorator](./decorator.js) 11 | 12 | * [facade](./facade.js) 13 | 14 | * [factory](./factory.js) 15 | 16 | * [flyweight](./flyweight.js) 17 | 18 | * [iterator](./iterator.js) 19 | 20 | * [mediator](./mediator.js) 21 | 22 | * [memento](./memento.js) 23 | 24 | * [mixins](./mixins.js) 25 | 26 | * [module](./module.js) 27 | 28 | * [mvc](./mvc.js) 29 | 30 | * [observer](./observer.js) 31 | 32 | * [prototype](./prototype.js) 33 | 34 | * [proxy](./proxy.js) 35 | 36 | * [singleton](./singleton.js) 37 | 38 | * [visitor](./visitor.js) 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /command.js: -------------------------------------------------------------------------------- 1 | //Remarks: Taken from feedback on Reddit. 2 | //The only reason to have the Command Pattern is if your language doesn't have functions/lambdas as a first-class construct. 3 | //A Command object is a poor-mans function. 4 | //This is/was true of Java (depending on which version you're running), 5 | //but is not true of Javascript, making the Command Pattern in Javascript completely pointless. 6 | 7 | //Structure and example 8 | var commandPattern = (function(){ 9 | var commandSet = { 10 | doSomething: function(arg1, arg2) { 11 | return "This is argument 1 "+ arg1 + "and this is arg 2 "+ arg2; 12 | }, 13 | doSomethingElse: function(arg3) { 14 | return "This is arg 3 "+arg3; 15 | }, 16 | executeCommands: function(name) { 17 | return commandSet[name] && commandSet[name].apply( commandSet, [].slice.call(arguments, 1) ); //gives arguments list 18 | } 19 | } 20 | return commandSet; 21 | 22 | })(); 23 | 24 | commandPattern.executeCommands( "doSomethingElse", "Ferrari", "14523" ); 25 | commandPattern.executeCommands( "doSomething", "Ford Mondeo", "54323" ); 26 | -------------------------------------------------------------------------------- /constructor.js: -------------------------------------------------------------------------------- 1 | //Structure 2 | function aFunction(a,b) { 3 | this.a = a; 4 | this.b = b; 5 | } 6 | 7 | aFunction.prototype.protoFunction = function(){ 8 | return this.a; 9 | } 10 | 11 | var aA = new aFunction('a','b'); 12 | console.log(aA.protoFunction());//'a'; 13 | 14 | -------------------------------------------------------------------------------- /decorator.js: -------------------------------------------------------------------------------- 1 | //Structure 2 | function functionA () { 3 | this.a = function() { return 'a'; } 4 | } 5 | 6 | function describeA ( anA ) { 7 | var aa = anA.a(); 8 | 9 | anA.a = function () { 10 | return 'An "' + aa + '" is the first alphabet in English and the most important one.' 11 | } 12 | } 13 | 14 | var anA = new functionA(); 15 | 16 | describeA( anA );//here aa='a' 17 | 18 | var output = anA.a(); 19 | 20 | console.log(output); //'An a is the first alphabet in English and the most important one.' 21 | 22 | -------------------------------------------------------------------------------- /facade.js: -------------------------------------------------------------------------------- 1 | //Structure and example 2 | //Facading hides complexities from the user. 3 | var mouse = (function() { 4 | var privates = { 5 | getActivity: function(act) { 6 | var activity = act.toLowerCase(); 7 | if(activity === 'click') { 8 | return "User is clicking"; 9 | } else if (activity === 'hover') { 10 | return "User is hovering"; 11 | } else if (activity === 'rightclick') { 12 | return "User right clicked"; 13 | } else if (activity === 'scroll') { 14 | return "User scrolled" 15 | } else { 16 | return "Unrecognised activity"; 17 | } 18 | } 19 | } 20 | 21 | return { 22 | facade: function(activity) { 23 | return privates.getActivity(activity); 24 | } 25 | } 26 | })(); 27 | 28 | console.log(mouse.facade('hover')); //User is hovering 29 | 30 | -------------------------------------------------------------------------------- /factory.js: -------------------------------------------------------------------------------- 1 | // Credit to Yehuda Katz for `fromPrototype` function 2 | // http://yehudakatz.com/2011/08/12/understanding-prototypes-in-javascript/ 3 | var fromPrototype = function(prototype, object) { 4 | var newObject = Object.create(prototype); 5 | for (var prop in object) { 6 | if (object.hasOwnProperty(prop)) { 7 | newObject[prop] = object[prop]; 8 | } 9 | } 10 | return newObject; 11 | }; 12 | 13 | 14 | // Define our `DeviceFactory` base object 15 | var DeviceFactory = { 16 | screen: function() { 17 | return 'retina'; 18 | }, 19 | battery: function() { 20 | return 'lithium ion battery'; 21 | }, 22 | keypad: function() { 23 | return 'keyboard'; 24 | }, 25 | processor: function() { 26 | return 'Intel Core-i5'; 27 | } 28 | }; 29 | 30 | // Extend `DeviceFactory` with other implementations 31 | DeviceFactory.makeLaptop = function() { 32 | return fromPrototype(DeviceFactory, { 33 | screen: function() { 34 | return 'retina 13 inches'; 35 | }, 36 | battery: function() { 37 | return 'lithium ion 9 hours battery'; 38 | }, 39 | keypad: function() { 40 | return 'backlit keyboard'; 41 | }, 42 | processor: function() { 43 | return 'Intel Core-i5' 44 | } 45 | }); 46 | }; 47 | 48 | DeviceFactory.makeSmartPhone = function() { 49 | return fromPrototype(DeviceFactory, { 50 | screen: function() { 51 | return 'retina 5 inches'; 52 | }, 53 | battery: function() { 54 | return 'lithium ion 15 hours'; 55 | }, 56 | keypad: function() { 57 | return 'touchscreen keypad'; 58 | }, 59 | processor: function() { 60 | return 'ARMv8' 61 | } 62 | }); 63 | }; 64 | 65 | DeviceFactory.makeTablet = function() { 66 | return fromPrototype(DeviceFactory, { 67 | screen: function() { 68 | return 'retina 9 inches'; 69 | }, 70 | battery: function() { 71 | return 'lithium ion 15 hours'; 72 | }, 73 | keypad: function() { 74 | return 'touchscreen keypad'; 75 | }, 76 | processor: function() { 77 | return 'ARMv8' 78 | } 79 | }); 80 | }; 81 | 82 | 83 | var appleMacbookPro = DeviceFactory.makeLaptop(); 84 | console.log(appleMacbookPro.screen()); // returns 'retina 13 inches'; 85 | var iPhoneSomeS = DeviceFactory.makeSmartPhone(); 86 | var iPadSomeSS = DeviceFactory.makeTablet(); 87 | -------------------------------------------------------------------------------- /flyweight.js: -------------------------------------------------------------------------------- 1 | //only common data here is model and brand, 2 | //and created a flyweight object, that saves memory 3 | var Car = function(model, brand) { 4 | this.model = model; 5 | this.brand = brand; 6 | } 7 | 8 | //carFactory using the common car model/method 9 | var carFactory = (function() { 10 | var existingCars = {}, existingCar; 11 | return { 12 | createCar: function(model, brand) { 13 | existingCar = existingCars[model]; 14 | if (!!existingCar) { 15 | return existingCar; 16 | } 17 | var car = new Car(model, brand); 18 | existingCars[model] = car; 19 | return car; 20 | } 21 | } 22 | })(); 23 | 24 | //carProductionManager using the common car model/method 25 | var carProductionManager = (function() { 26 | var carDb = {}; 27 | return { 28 | addCar: function(carId, model, brand, color, carType){ 29 | var car = carFactory.createCar(model, brand); 30 | carDb[carId] = { 31 | color: color, 32 | type: carType, 33 | car: car 34 | } 35 | }, 36 | repaintCar: function(carId, newColor) { 37 | var carData = carDb[carId]; 38 | carData.color = newColor 39 | } 40 | } 41 | })(); 42 | -------------------------------------------------------------------------------- /iterator.js: -------------------------------------------------------------------------------- 1 | // Access elements of a collection sequentially without 2 | // needing to know the underlying representation. 3 | 4 | /************ 5 | Iterator 6 | ************/ 7 | function Iterator(arr) { 8 | var currentPosition = -1; 9 | 10 | return { 11 | hasNext:function() { 12 | return currentPosition+1 < arr.length; 13 | }, 14 | next: function() { 15 | if(!this.hasNext()) 16 | return null; 17 | currentPosition++; 18 | return arr[currentPosition]; 19 | } 20 | } 21 | } 22 | 23 | // Example Usage 24 | var people = [{id:1,name:'John'}, {id:2,name:'George'}, {id:3,name:'Guy'}]; 25 | var peopleIterator = Iterator(people); // Create Iterator for 'people' 26 | while(peopleIterator.hasNext()) { 27 | var person = peopleIterator.next(); 28 | console.log(person.name + '\'s id is: ' + person.id + '!'); 29 | } 30 | 31 | // John's id is: 1! 32 | // George's id is: 2! 33 | // Guy's id is: 3! 34 | -------------------------------------------------------------------------------- /mediator.js: -------------------------------------------------------------------------------- 1 | // Mediator pattern is used to reduce communication 2 | // complexity between multiple objects or classes. 3 | function Mediator() { 4 | var users = []; 5 | 6 | return { 7 | addUser: function(user) { 8 | users.push(user); 9 | }, 10 | // Message sending business logic 11 | publishMessage: function(msg, receiver) { 12 | if(receiver) { 13 | receiver.messages.push(msg); 14 | } 15 | else { 16 | users.forEach(function(user) { 17 | user.messages.push(msg); 18 | }); 19 | } 20 | } 21 | } 22 | } 23 | 24 | // Usage Example 25 | var mediator = Mediator(); // Initialize mediator 26 | // Define user class 27 | function User(name) { 28 | this.name = name; 29 | this.messages = []; 30 | mediator.addUser(this); 31 | } 32 | User.prototype.sendMessage = function(msg, receiver) { 33 | msg = '[' + this.name + ']: ' + msg; 34 | mediator.publishMessage(msg,receiver); 35 | } 36 | 37 | // Initialize users 38 | var u1 = new User('Donald'); 39 | var u2 = new User('Peter'); 40 | var u3 = new User('Anna'); 41 | // Message sending 42 | u1.sendMessage('Hi, anybody here?'); 43 | u2.sendMessage('Hi Donald, nice to meet you.', u1); 44 | u3.sendMessage('Hi Guys!'); 45 | -------------------------------------------------------------------------------- /memento.js: -------------------------------------------------------------------------------- 1 | // Memento pattern is used to restore state of an object to a previous state. 2 | function Memento(initialState) { 3 | var state = initialState || null; 4 | var stateList = state ? [state] : []; 5 | 6 | return { 7 | getState: function() { 8 | return state; 9 | }, 10 | getStateList: function() { 11 | return stateList; 12 | }, 13 | get: function(index) { 14 | if(index > -1 && index < stateList.length) { 15 | return stateList[index]; 16 | } 17 | else { 18 | throw new Error('No state indexed ' + index); 19 | } 20 | }, 21 | addState: function(newState) { 22 | if(!newState) 23 | throw new Error('Please provide a state object'); 24 | state = newState; 25 | stateList.push(newState); 26 | } 27 | } 28 | } 29 | 30 | // Helper function used to deep copy an object using jQuery 31 | function copy(obj) { 32 | return jQuery.extend(true, {}, obj); 33 | } 34 | 35 | // Example Usage, please notice that using this pattern, you should 36 | // not mutate objects or arrays, but clone them, since they are passed 37 | // by reference in javascript. 38 | // If your state is a string or a number however, you may mutate it. 39 | var songs = { 40 | Queen: ['I want to break free', 'Another on bites the dust', 'We will rock you'], 41 | Scorpins: ['Still loving you', 'Love will keep us alive', 'Wind of change'], 42 | Muse: ['Butterflies and hurricanes', 'Starlight', 'Unintended'], 43 | BeeGees: ['How deep is your love', 'Staying alive'] 44 | } 45 | 46 | var memento = Memento(copy(songs)); // Initialize Memento 47 | songs.BeeGees.push('Too much heaven'); 48 | songs.Muse.push('Hysteria'); 49 | memento.addState(copy(songs)); // Add new state to memento 50 | songs['Abba'] = ['Mama mia', 'Happy new year']; 51 | songs['Eric Clapton'] = ['Tears in heaven', 'Bell bottom blues']; 52 | memento.addState(copy(songs)); // Add new state to memento 53 | console.log(memento.getStateList()); // log state list 54 | console.log(memento.getState()); // log current state 55 | console.log(memento.get(1)); // log second state 56 | songs = memento.get(0); // set songs to initial state 57 | memento.addState(copy(songs)); // Add new old state to memento 58 | console.log(memento.getStateList()); // log state list 59 | -------------------------------------------------------------------------------- /mixins.js: -------------------------------------------------------------------------------- 1 | //Structure and Example 2 | var mixins = { 3 | get: function(){ 4 | console.log( "get this item" ); 5 | }, 6 | set: function(){ 7 | console.log( "set this item" ); 8 | }, 9 | delete: function(){ 10 | console.log( "delete it right away!" ); 11 | } 12 | }; 13 | 14 | // Another skeleton constructor 15 | function aConstructor(){ 16 | this.thatIsAllFolks = function(){ 17 | console.log('Nope! I refuse to do anything anymore!!!') 18 | }; 19 | } 20 | 21 | // Extend both protoype with our Mixin 22 | for(var key in mixins) aConstructor.prototype[key] = mixins[key]; 23 | 24 | // Create a new instance of aConstructor 25 | var myMixinConstructor = new aConstructor(); 26 | 27 | myMixinConstructor.get(); //get this item 28 | myMixinConstructor.thatIsAllFolks(); //Nope! I refuse to do anything anymore!!! 29 | -------------------------------------------------------------------------------- /module.js: -------------------------------------------------------------------------------- 1 | //Structure 2 | var a = (function(){ 3 | //private variables & methods 4 | var privateVar = 'X'; 5 | //public methods and variables 6 | return { 7 | getX: function(){ 8 | return privateVar; 9 | } 10 | } 11 | })(); 12 | 13 | a.getX(); //X 14 | 15 | -------------------------------------------------------------------------------- /mvc.js: -------------------------------------------------------------------------------- 1 | // Perhaps the most used pattern in javascript web development. 2 | // MVC(Model-View-Controller) seperates data model representation 3 | // from data visuallization. 4 | // The glue that connects the model changes and view updates 5 | // is the contoller. 6 | 7 | 8 | // Define Account Model 9 | function AccountModel(owner, balance) { 10 | this.owner = owner; 11 | this.balance = balance; 12 | } 13 | AccountModel.prototype = { 14 | getOwner: function() { 15 | return this.owner; 16 | }, 17 | getBalance: function() { 18 | return this.balance; 19 | } 20 | } 21 | 22 | // Define Account View 23 | function AccountView() {} 24 | AccountView.prototype.showAccountDetails = function(owner, balance) { 25 | console.log('Account owner: ' + owner); 26 | console.log('Account balance: ' + balance); 27 | } 28 | 29 | // Define Account Controller 30 | function AccountController(model, view) { 31 | this.accountView = function() { 32 | view.showAccountDetails(model.getOwner(), model.getBalance()); 33 | } 34 | this.deposit = function(amount) { 35 | this.setBalance(model.getBalance() + amount); 36 | } 37 | this.discount = function(amount) { 38 | this.setBalance(model.getBalance() - amount); 39 | } 40 | this.setOwner = function(newOwner) { 41 | model.owner = newOwner; 42 | this.accountView(); 43 | } 44 | this.setBalance = function(newBalance) { 45 | model.balance = newBalance; 46 | this.accountView(); 47 | } 48 | } 49 | 50 | // Usage Example 51 | var controller = new AccountController(new AccountModel('Ben', 15000), new AccountView()); 52 | controller.accountView(); 53 | // Account owner: Ben 54 | // Account balance: 15000 55 | controller.deposit(500); 56 | // Account owner: Ben 57 | // Account balance: 15500 58 | controller.discount(120); 59 | // Account owner: Ben 60 | // Account balance: 15380 61 | controller.setOwner('Bob'); 62 | // Account owner: Bob 63 | // Account balance: 15380 64 | controller.setBalance(0); 65 | // Account owner: Bob 66 | // Account balance: 0 67 | controller.deposit(1000000); 68 | // Account owner: Bob 69 | // Account balance: 1000000 70 | -------------------------------------------------------------------------------- /observer.js: -------------------------------------------------------------------------------- 1 | function theSubject(){ 2 | this.handlerList = []; 3 | } 4 | 5 | theSubject.prototype = { 6 | addObserver: function(obs) { 7 | //add all the observers in an array. 8 | this.handlerList.push(obs); 9 | console.log('added observer', this.handlerList); 10 | }, 11 | removeObserver: function(obs) { 12 | //remove given observer from the array. 13 | for ( var ii=0, length = this.handlerList.length; ii 5000) { 80 | score += 0.5; 81 | } 82 | return score; 83 | }, 84 | getHotelRoomsVolume: function() { 85 | return hotel.numberOfRooms * hotel.avgRoomSize; 86 | } 87 | }); 88 | 89 | // Return extended instance 90 | return hotel; 91 | } 92 | 93 | // Usage example 94 | var hp = HotelProxy(4, true, false, 2800, 150); 95 | console.log(hp.getScore()); // 7 96 | console.log(hp.getHotelRoomsVolume()); // 420000 97 | hp.setAvgRoomSize(160); 98 | console.log(hp.getHotelRoomsVolume()); // 448000 99 | hp.setStars(5); 100 | console.log(hp.getScore()); //8 101 | -------------------------------------------------------------------------------- /singleton.js: -------------------------------------------------------------------------------- 1 | //Structure 2 | var mySingleton = (function() { 3 | 4 | function init(options) { 5 | //some private variables 6 | var x = '1', y = 2, z = 'Abc', pi = Math.PI; 7 | //return public methods(accessing private variables if needed.) 8 | return { 9 | X : x, 10 | getPi : function() { 11 | return pi; 12 | } 13 | } 14 | } 15 | //There must be exactly one instance of a class, 16 | //and it must be accessible to clients from a well-known access point 17 | var instanceOfSingleton; 18 | return { 19 | initialize: function(options) { 20 | //initialize only if not initialized before 21 | if(instanceOfSingleton === undefined) { 22 | instanceOfSingleton = init(options); 23 | } 24 | return instanceOfSingleton; 25 | } 26 | }; 27 | 28 | })(); 29 | 30 | var singleton = mySingleton.initialize(); 31 | console.log(singleton.X); //'1' 32 | console.log(singleton.getPi());//3.141592653589793 33 | 34 | 35 | -------------------------------------------------------------------------------- /visitor.js: -------------------------------------------------------------------------------- 1 | // Visitor lets you define a new operation without changing 2 | // the class of the elements on which it operates. 3 | 4 | // Define 'Counter' Class 5 | var Counter = function(initVal) { 6 | this.count = initVal || 0; 7 | }; 8 | 9 | Counter.prototype.increment = function() { 10 | this.count++; 11 | }; 12 | 13 | // Let a visitor access any properties of a 14 | // 'Counter' instance by calling ìts 'visit' 15 | // function on 'this' 16 | Counter.prototype.accept = function(visitor) { 17 | visitor.visit.call(this); 18 | }; 19 | 20 | // Create a visitor object that will decrement 21 | // the counter 22 | var visitor = { 23 | visit: function() { 24 | this.count--; 25 | } 26 | }; 27 | 28 | // Usage example 29 | var counter = new Counter; 30 | 31 | counter.accept(visitor); 32 | console.log(counter.count); // -1 33 | 34 | // Alternatively, one might permanently bind 35 | // the visit funtion to the 'Counter' instance 36 | Counter.prototype.accept = function(visitor) { 37 | visitor.visit = visitor.visit.bind(this); 38 | }; 39 | 40 | counter.accept(visitor); 41 | visitor.visit(); 42 | console.log(counter.count); // -2 --------------------------------------------------------------------------------