object oriented javascript
DESCRIPTION
My talk for AJAXWorld 2009 in NYC.TRANSCRIPT
Object Oriented JavaScriptMike Girouard — AJAXWorld 2009
Mike Girouard — AJAXWorld 2009
Today’s Agenda
JavaScript Object ModelTypes, Constructors, Inheritance
“Missing” FeaturesNamespaces, Visibility, Polymorphism
Useful Design PatternsFactories, Singletons, Modules
Mike Girouard — AJAXWorld 2009
The JavaScript Object Model
Mike Girouard — AJAXWorld 2009
Boolean
Object
Number String Array Date
RegExp Function Math Error EvalError
RangeError ReferenceError SyntaxError TypeError URIError
Primitive Types
Mike Girouard — AJAXWorld 2009
Creating Custom Types
Object constructors are functions
Defining a new type is as simple as defining a function
Creating a new instance is simply invoking a function with the new prefix
Mike Girouard — AJAXWorld 2009
// Define the constructorvar Timestamp = function () { this.value = new Date().getTime();};
// Instantiatevar ts = new Timestamp;
// Read instance variableconsole.log(ts.value);
Creating Custom Types
Mike Girouard — AJAXWorld 2009
Instance Members
Instance members are defined in a constructor’s prototype object
New instances will have access to prototype members
Prototypes may be modified at runtime
Mike Girouard — AJAXWorld 2009
var Timestamp = function () { this.value = new Date().getTime();};
Timestamp.prototype.getValue = function () { return this.value;};
var ts = new Timestamp;console.log(ts.getValue());
Instance Members
Mike Girouard — AJAXWorld 2009
var Timestamp = function () { this.value = new Date().getTime();};
var ts = new Timestamp;
Timestamp.prototype.getValue = function () { return this.value;};
console.log(ts.getValue()); // Still works
Instance Members
Mike Girouard — AJAXWorld 2009
Inheritance
Mike Girouard — AJAXWorld 2009
Prototypal Inheritance
Completely eliminates the need for classes
Objects inherit directly from other objects (prototypes)
Incredibly efficient, ridiculously strange
Mike Girouard — AJAXWorld 2009
var Foo = function () {}; // Foo Constructorvar Bar = function () {}; // Bar ConstructorBar.prototype = new Foo; // Bar extends Foo
var f = new Foo(); // Foo instancevar b = new Bar(); // Bar instance
console.log(f instanceof Foo); // trueconsole.log(f instanceof Bar); // falseconsole.log(b instanceof Foo); // trueconsole.log(b instanceof Bar); // true
Prototypal Inheritance
Mike Girouard — AJAXWorld 2009
Classical Inheritance
JavaScript has no native support for classical inheritance
Many libraries support class-like structures
Rolling your own is quite trivial
Mike Girouard — AJAXWorld 2009
var Foo = classFactory({ __construct: function () { this.identify(); },
identify: function () { console.log(‘Foo’); }});
Classical Inheritance
Mike Girouard — AJAXWorld 2009
var Bar = Foo.extend({ identify: function () { console.log(‘Bar’); }});
Classical Inheritance
Mike Girouard — AJAXWorld 2009
var classFactory = function (obj, extends) { if (extends) for (var i in extends) if (!obj[i]) obj[i] = extends[i];
if (obj.__construct) obj.__construct.call(obj); obj.extend = function (subclass) { return classFactory(subclass, obj); };
return obj;};
Classical Inheritance
Mike Girouard — AJAXWorld 2009
“Missing” Features
Mike Girouard — AJAXWorld 2009
Namespaces
Mike Girouard — AJAXWorld 2009
Why bother?
JavaScript has implied global scope
Global variables are only for selfish people
Raise your hand if you use these variable names:
id, element, name, value, target…
Mike Girouard — AJAXWorld 2009
Implementing namespaces
Use an object… any object
Remember:Objects can store any kind of value
Everything is an object
This means anything [mutable] can be a namespace
Mike Girouard — AJAXWorld 2009
var mikeg = { name : ‘Mike G’, location : ‘NYC’,
getName : function () { return this.name; }, getLocation : function () { return location; }};
Namespace Objects
Mike Girouard — AJAXWorld 2009
var getXHR = function () { if (!getXHR.enabled) return null; var xhr = new XMLHttpRequest; getXHR.registry.push(xhr);
return xhr;};
getXHR.registry = [];getXHR.enabled = true;
Namespace Functions
Mike Girouard — AJAXWorld 2009
Visibility
Mike Girouard — AJAXWorld 2009
Data Hiding in JS
There is no concept of public, private, or protected in JavaScript
Closures allow values to be remembered in a function, even after it terminates
Mike Girouard — AJAXWorld 2009
var Person = function (name) { this.getName = function () { return name; };
this.setName = function (newName) { return name = newName; };};
Data Hiding in JS
Mike Girouard — AJAXWorld 2009
// Assume: Person
var mike = new Person(‘Mike G.’);var alex = new Person(‘Alex H.’);
console.log(mike.name); // undefinedconsole.log(alex.name); // undefined
console.log(mike.getName()); // Mike G.console.log(alex.getName()); // Alex H.
Data Hiding in JS
Mike Girouard — AJAXWorld 2009
Polymorphism
Mike Girouard — AJAXWorld 2009
Easier than you think…
Because JavaScript is a dynamic language, polymorphism is quite easy and very common.
Two common types of polymorphism:1. Runtime Replacement
2. Loadtime Branching
Mike Girouard — AJAXWorld 2009
var getXHR = function () { if (window.XMLHttpRequest) { return function () { // Return a standard XHR instance }; } else { return function () { // Return an Explorer XHR instance }; }}(); // Note: parens trigger self-invocation
Loadtime Branching
Mike Girouard — AJAXWorld 2009
var documentListFactory = function () { var out = []; // Just a simple array
// Override the default .push() method out.push = function (document) { Array.prototype.push.call(out, { document : document, timespamp : new Date().getTime() }); };
return out;};
Runtime Replacement
Mike Girouard — AJAXWorld 2009
Useful Design Patterns
Mike Girouard — AJAXWorld 2009
Factories
Mike Girouard — AJAXWorld 2009
You Need Factories
Forgetting the new keyword will break your application.
The issue comes down to the implied global scope
Using new, context = the instance
Forgetting new, context = window
Mike Girouard — AJAXWorld 2009
var DocumentCreator = function (document) { // What does ‘this’ refer to? this.document = document;};
new DocumentCreator(‘foo.txt’); // InstanceDocumentCreator(‘foo.txt’); // window
Brittle Constructor
Mike Girouard — AJAXWorld 2009
var DocumentCreator = function (document) { // Yes, constructors can have return values :) return DocumentCreator.factory(document);};
DocumentCreator.factory = function (document) { return new DocumentCreator(document);};
new DocumentCreator(‘foo.txt’); // InstanceDocumentCreator(‘foo.txt’); // Instance
A Simple Factory
Mike Girouard — AJAXWorld 2009
Singletons
Mike Girouard — AJAXWorld 2009
Singletons are Easy Too
Nothing more than a simple object literal
Objects are always passed by reference and values are static
Very useful for grouping items together, even if they exist in other objects
Mike Girouard — AJAXWorld 2009
var myHttpLibrary = { newXHR : function (params) { ... }, parseQueryString : function (qs) { ... }, createQueryString : function (qs) { ... }};
Singletons
Mike Girouard — AJAXWorld 2009
The Module
Mike Girouard — AJAXWorld 2009
A Visibility Pattern
Original pattern discovered by Douglas Crockford
Simplifies private data in JS
Mike Girouard — AJAXWorld 2009
var config = function () { var data = {}; return { get : function (name) { return data[name]; }, set : function (name, value) { data[name] = value; return this.get(name); } };}();
A Simple Module
Mike Girouard — AJAXWorld 2009
// Assume: config
config.set(‘name’, ‘Mike G.’);
console.log(config.data.name); // undefinedconsole.log(config.get(‘name’)); // Mike G.
A Simple Module
Mike Girouard — AJAXWorld 2009
var config = function () { var me, data = {}; return me = { get : function (name) { return data[name] }, set : function (name, value) { data[name] = value; return me.get(name); } };}();
A Better Module
Mike Girouard — AJAXWorld 2009
var module = function () { var app, util, service;
app = {};
util = {};
return service = {};}();
Modules for Organization
Mike Girouard — AJAXWorld 2009
var module = function () { var app, util, service;
app = { init : function () { ... } };
util = {};
return service = { init : app.init };}();
Modules for Organization
Mike Girouard — AJAXWorld 2009
Any questions so far?
Mike Girouard — AJAXWorld 2009
Miscellaneous…
Everything in JavaScript is an object
All objects can be enumerated via for…in
The for…in construct sees all the way up the prototype chain
myObject.hasOwnProperty(‘foo’) is gross but necessary
Mike Girouard — AJAXWorld 2009
Miscellaneous…
Most primitives have literals
Primitive constructors aren’t called in literals (IE: no prototype features added)
Please don’t go modifying primitive prototypes
Mike Girouard — AJAXWorld 2009
Miscellaneous…
Forgetting to use the new keyword in some cases will break your app.
The typeof operator is useful, but lies
The instanceof operator is more accurate
Mike Girouard — AJAXWorld 2009
Miscellaneous…
The use of this tempting, but introduces ambiguity into your program
In most cases its not necessary and can be avoided
Mike Girouard — AJAXWorld 2009
Miscellaneous…
Most of JavaScript’s OO features are clever functional programming patterns
Studying functional programming languages will make you a better JavaScript programmer
Thank [email protected]