Javascript OO Cheat Sheet
/***********************************************************************************************************************
***********************************************************************************************************************
* CONTENTS:
* Native Object
* Object Literal
* Basic Object
* Psuedo-Class
* Self Executing/Invoking Structure
* Lazy Function
* Module Pattern
* Reveal Module Pattern
* Singleton Module
* "Real" Singleton
* Widget Pattern
* Curried Function
* JR's Simple Javascript Inheritance
* jQuery Plugin Pattern
***********************************************************************************************************************
***********************************************************************************************************************/
/***********************************************************************************************************************
* Object
* Work with Intellij Structure but Object Literal works slightly better
*/
var NewObject = new Object();
NewObject.foo = 123;
NewObject.bar = 123;
NewObject.method = function(){
//do stuff
};
/***********************************************************************************************************************
* Object Literal (Static Class, psuedo-Singleton)
* Object Literal is often confused with Singleton, it's similar to a Static Class
* Object Literals don't have Function.prototype and Function.prototype.constructor
* Singletons are instance of a class which can only be create once
*/
var ObjectLiteral = {
foo : 123,
bar : 456,
method : function() {
//do stuff
}
};
/***********************************************************************************************************************
* Simple Javascript Psuedo-Class (Object Constructor, Object Prototype and Prototype Chaining)
* Technically these are Object Constructors not Classes since classes implies datatypes which javascript doesn't support
* Reference:
* http://helephant.com/2008/09/constructor-functions/
* http://helephant.com/2009/01/javascript-object-prototype/
* http://helephant.com/2009/08/javascript-prototype-chaining/
*
* Pros:
* Javascript 101 so it's easy to understand.
* No External libraries or custom create, init, extend or inherit needed
* Works well with IDE's code completion
* Cons:
* Classically trained people don't like it
*
* Sort of works with Intellij Structure...
* Occassionally it gets confused and loses the Constructors properties and methods
*/
function SimpleClass() {
this.x = 123;
this.y = 456;
this.instanceMethod = function() {
//Specific to the instance
}
}
/** Caution: Object Prototype Overwrite
* NOTE don't use object literal. It overwrites the SimpleClass.prototype.constructor
* SimpleClass.prototype = {
* foo = function(){},
* bar = function(){}
* }
* Fix for the constructor.
* SimpleClass.prototype = {
* foo : function(){},
* bar : function(){}, `
* constructor : SimpleClass
* }
*/
SimpleClass.prototype.protoMethod = function() {
//Same across all object of the same class
};
/**
* a static function (aka class or shared function) lives on the Object but does not have access to the instance.
* http://www.komodomedia.com/blog/2008/09/javascript-classes-for-n00bs
* It seems a bit superfluous since we can create a prototype function
**/
SimpleClass.staticMember = 555;
SimpleClass.staticFunction = function() {
return new Date();
};
var simpleObject = new SimpleClass();
simpleObject.foo();
SimpleSubClass.prototype = new A;
SimpleSubClass.prototype.constructor = SimpleSubClass;
function SimpleSubClass() {
SimpleClass.call(this);
this.z = 555;
}
SimpleSubClass.prototype.protoMethod = function() {
//Overrides SimpleClass.protoMethod
SimpleClass.prototype.protoMethod.call(this); //call super class method
};
SimpleSubClass.prototype.protoMethodSub = function() {
//define new SimpleSubClass method
};
var simpleSubObject = new SimpleSubClass();
console.log(simpleSubObject instanceof SimpleObject); //true
console.log(simpleSubObject instanceof SimpleSubObject); //true
/***********************************************************************************************************************
* Self Executing/Invoking Anonymous Function
* Good for intensive computations once at loadtime
* Work with Intellij Structure
**/
var selfExecuting = (function() {
var result = true;
//result = "intensive calculation goes here"
return result;
})();
console.log(selfExecuting);
/***********************************************************************************************************************
* Lazy Function Pattern using closure
* Good for intensive computations once at runtime
* http://michaux.ca/articles/lazy-function-definition-pattern
* Work with Intellij Structure
**/
function lazyOuter() {
var closed = new Date();
//closed = "intensive calculation goes here"
return function lazyInner() {
console.log(closed);
};
}
//First call to outer gets date and returns a new function which "closes-over" the date
//All subsequent calls to "outer" are actually calling "inner" so the date is the same.
outer();
outer();
outer();
/***********************************************************************************************************************
* Module Pattern by Cornford/Crockford
* Module Pattern is often confused with Singleton. Modules don't have Function.prototype and Function.prototype.constructor
* http://jibbering.com/faq/notes/closures/#clEncap
* http://www.crockford.com/javascript/private.html
* Work surprisingly well with Intellij Structure
**/
var Module = (function() {
var privateVar;
function privateMethod() {
// ...
}
return { // public interface
publicMethod1: function () {
// private members can be accessed here
},
publicMethod2: function () {
// ...
}
};
})();
/***********************************************************************************************************************
* Reveal Module Pattern:
* Same as the Module patterns but returns an object literal which maps public methods to private functions
* http://www.wait-till-i.com/2007/08/22/again-with-the-module-pattern-reveal-something-to-the-world/
* Work surprisingly well with Intellij Structure
**/
var RevealModule = (function() {
var privateVar;
function privateMethod() {
// ...
}
function publicMethod1() {
// private members can be accessed here
}
function publicMethod2() {
// ...
}
return { // public interface
pub1 : publicMethod1,
pub2 : publicMethod2
};
})();
/***********************************************************************************************************************
* Singleton Using a module, closure and function redefinition
* Allows for private variables and function closed in scope
* Function.constructor property for debugger
* http://stackoverflow.com/questions/1895635/javascript-singleton-question
*/
//This version isn't preferred since it is confusing to follow and causes problems with debugging.
/*function SingletonModule() {
var instance = (function() {
var privateVar;
function privateMethod() {
// ...
}
return { // public interface
publicMethod1: function () {
// private members can be accessed here
},
publicMethod2: function () {
// ...
}
};
})();
SingletonModule = function SingletonModule() { // re-define the function for subsequent calls
return instance;
};
// call the new function
return SingletonModule();
}*/
//console.log(singleton)
//console.log(typeof singleton == 'function')
//console.log(singleton() == singleton())
//console.log(singleton() === singleton())
//console.log(singleton.prototype)
//console.log(singleton.prototype.constructor)
//console.log(singleton.name)
/***********************************************************************************************************************
* Real Singletons
* Allows for private variables and function closed in scope
* Singletons are instantiated from classes so they have prototype chains and constructor reference
* http://stackoverflow.com/questions/1895635/javascript-singleton-question
*
* Confuses Intellij Structure.
* singletion() appears as a private despite being global
* methods and properties are not shown
*/
(function (global) {
var singleton;
function Singleton () {
// singleton does have a constructor that should only be used once
this.foo = "bar";
delete Singleton; // disappear the constructor if you want
}
Singleton.prototype.log = function(){
console.log('hello, ', this)
};
global.singleton = function singleton() {
return singleton || (singleton = new Singleton());
};
})(window);
console.log(singleton)
console.log(typeof singleton == 'function')
console.log(singleton() == singleton())
console.log(singleton() === singleton())
console.log(singleton.prototype)
console.log(singleton.prototype.constructor)
console.log(singleton.name)
/***********************************************************************************************************************
* Javascript Widget Pattern Using the Module Pattern
* http://michaux.ca/articles/how-i-write-javascript-widgets
* NOTE: this pattern is used by John Resig for SimpleJavaScriptInheritance
*
* Pros
* "global" is references "this" which is returned to the global namespace
* Anything attached to "globaL" is publically available but not internally available
* Provides an additional closure level for helper functions
*
* Cons:
* More difficult to understand and debug
* Confuse Intellij Structure
* global and helperFunction appear as private
* PublicObject doesn't appear at all
*/
(function() {
var global = this;
// helper functions
var helperFunction = function(msg) {
//...
};
// widget constructor function
global.PublicObject = function() {
// private instance methods
var privateMethod = function() {
//..
};
var anotherPrivate = function() {
//..
};
// public instance methods
return {
publicMethod1 : function() {return privateMethod;},
publicMethod2 : anotherPrivate
};
};
})();
/***********************************************************************************************************************
* Curried Function
* http://flesler.blogspot.com/2008/11/haskell-functions-for-javascript.html
* http://www.svendtofte.com/code/curried_javascript/
* Essentially they are partially executed functions. Or in javascripts case, they are functions which are redefined
* The new returned function will accept the additional missing parameter to complete the functions purpose.
* Pros: Convenient tool for certain circumstances
* Cons: Not widely understood
*
* Sort of works with Intellij. The primary function works, but subsequent references are lost
*/
function add(a, b) {
if (arguments.length < 1) {
return add;
} else {
if (arguments.length < 2) {
return function(c) {
return a + c
}
} else {
return a + b;
}
}
}
//console.log(add(2, 2)); // alerts 4
//var adds4 = add(4); // adds4, is now a function,
//// which adds 4, to it's argument.
//console.log(adds4(5)); // alerts 9.
/***********************************************************************************************************************
* John Resig's Simple JavaScript Inheritance
* Works fairly well with Intellij Structure
* Intellij Structure fails to recognize super members in the inherited classes
* IOW: Intellij Structure does not show Ninja.foo() (.foo() is inherited from Person)
*/
var Person = Class.extend({
init: function(isDancing) {
this.dancing = isDancing;
},
foo: function(){}
});
var Ninja = Person.extend({
init: function() {
this._super(false);
},
bar: function(){}
});
var person = new Person(true);
console.log(person.dancing); // => true
var ninja = new Ninja();
console.log(ninja.dancing); // => false
/***********************************************************************************************************************
* jQuery Basic Plugin Pattern
* http://docs.jquery.com/Plugins/Authoring
*/
(function( $ ){
$.fn.tooltip = function( options ) {
var settings = {
'location' : 'top',
'background-color' : 'blue'
};
return this.each(function() {
// If options exist, lets merge them
// with our default settings
if ( options ) {
$.extend( settings, options );
}
// Tooltip plugin code here
});
};
})( jQuery );
/******************
*
* Full jQuery plugin template
* http://docs.jquery.com/Plugins/Authoring
*
*********************/
(function ($) {
var methods = {
init: function (options) {
var settings = $.extend({
'location': 'top',
'background-color': 'blue'
}, options);
//NOTE: here the 'this' refers to the $ wrapped object
return this.each(function () {
//NOTE: here 'this' refers to the element, re-wrap and set to $this
var $this = $(this),
data = $this.data('tooltip'), //fetch existing data
tooltip = $('<div />', { //create elements
text: $this.attr('title')
});
// If the plugin hasn't been initialized yet
if (!data) {
/* Do more setup stuff here */
$(this).data('tooltip', { //store object literal on the data attribute of the element
target: $this,
tooltip: tooltip
});
}
$(window).bind('over.tooltip', methods.show); //bind events to element. NOTE the event namespace ".tooltip"
});
},
destroy: function () {
return this.each(function () {
var $this = $(this),
data = $this.data('tooltip'); //fetch existing data
$(window).unbind('.tooltip'); //remove (unbind) namespaced events from element
data.tooltip.remove(); //remove element stoted on data
$this.removeData('tooltip'); //remove data from tooltip
});
},
show: function () {
return this.each(function () {
var $this = $(this);
});
},
hide: function () {
return this.each(function () {
var $this = $(this);
});
},
update: function (content) {
return this.each(function () {
var $this = $(this);
});
}
};
$.fn.tooltip = function (method) {
// Method calling logic
if (methods[method]) {
return methods[ method ].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.tooltip');
}
};
})(jQuery);
/** jQuery UI Plugin Template
* http://jqueryui.com/docs/Developer_Guide
* Core widget template with some additional properties and comments for clarity
*/
$.widget("ui.mywidget", {
//options: instance level options
options: {
option1: "defaultValue",
hidden: true
},
//_create: called on construction
_create: function() {
var self = this;
//this: references the instance of the widget, commonly refferred to as 'self'
//this.element: references the jQuery object
// creation code for mywidget, can use this.options
if (self.options.hidden) {
// and this.element
self.element
.addClass('foobar')
.bind('eventname', function () {})
.hide();
}
},
//_init: called on construction and re-initialization
_init : function () {
this.refresh(); //offloading to refresh() is common in jQuery UI widgets
},
refresh : function() {
this.element.find('selector, selector')//update the HTML
.addClass('foobar');
},
_privateMethod: function() {
//private functions should be named with a leading underscore
},
pubicMethod: function() {
//public functions do not have an underscore
},
pubicMethodWithCallback: function(x,y,z,callback) {
var result = 1 + 1;
console.log(x, y, z, callback);
//_trigger looks for a callback on this.options['eventName']
//then triggers an event named <widgetName><eventName>
this._trigger('eventName', evt, result);
},
//combo getter/setter
value: function(newValue) {
if (newValue === undefined) {
return this._value();
}
this._setOption('value', newValue);
return this;
},
//_setOption: probably don't need to override the default _setOption
//but if you do then be sure to apply the original like this
_setOption : function (key, value) {
if (key === 'special') {
//do something special
}
$.Widget.prototype._setOption.apply(this, arguments); // default setOption
},
//destroy: returns the HTML to it's original state
destroy: function() {
this.element
.removeClass('foobar')//remove classes the widget added
.removeAttr(); //remove attributes the widget
$.Widget.prototype.destroy.apply(this, arguments); // default destroy
// now do other stuff particular to this widget
}
});
$.extend($.ui.mywidget, {
enhancement : function() {
}
});