JavaScript Patterns

Post on 08-Sep-2014

28269 Views

Category:

Technology

13 Downloads

Preview:

Click to see full reader

DESCRIPTION

Slides from Ajax Experience 2009. In this session: - Object creation patterns - Code reuse patterns - Functional patterns - More on object creation - Design patterns Some example patterns: object creation with literals and constructos, prototypes, inheritance and other code reuse patterns, lazy definition, callbacks, singleton, factory, classical and prototypal inheritance, namespaces, chaining, modules, static methods, private and privileged members For more information, see: http://jspatterns.com My column in the JavaScript Magazine (http://jsmag.com) Blog: http://phpied.com

Transcript

JavaScript Patterns Stoyan Stefanov, Yahoo! @stoyanstefanov

Ajax Experience, Boston 2009 3:25 p.m. Sept 15, 2009

About me •  Yahoo! Search •  Yahoo! Exceptional Performance •  YSlow 2.0 architect •  http://smush.it •  Books, articles •  http://phpied.com

Types of patterns

•  OO Design Patterns (gang of four) •  JavaScript-specific coding patterns •  Anti-patterns

In this session…

•  Object creation patterns •  Code reuse patterns •  Functional patterns •  More on object creation •  Design patterns

Object creation patterns

#

Two ways to create objects

•  Object literal •  Constructor functions

{} vs. new

Object literal

var adam = {}; // clean slate 

adam.name = “Adam”; adam.say = function() {   return “I am ” + this.name; }; 

Object literal var adam = {   name: “Adam”,   say: function() {     return “I am ” + this.name;   } }; 

Using a constructor

var adam = new Person(“Adam”); adam.say(); // “I am Adam” 

Constructor functions var Person = function(name) { 

  this.name = name;   this.say = function() {     return “I am ” + this.name;   }; 

}; 

Constructor functions var Person = function(name) {   // var this = {};   this.name = name;   this.say = function() {     return “I am ” + this.name;   };   // return this; }; 

Naming convention

MyConstructor myFunction 

Enforcing new function Person() {    var that = (this === window) ? {} : this; 

  that.name = name; 

  that.say = function() {     return “I am ” + that.name; 

  }; 

  return that;  }  

Enforcing new 

this instanceof Person 

this instanceof arguments.callee 

ES5 FTW

Prototype

var Person = function(name) { 

  this.name = name; 

}; 

Person.prototype.say = function() { 

  return “I am ” + this.name; 

}; 

How is the prototype used?

>>> var adam = new Person('Adam'); >>> adam.name; "Adam" >>> adam.say(); "I am Adam" 

If you want to reuse it, add it to the prototype

Code reuse patterns

Inheritance – friend or foe

“Prefer object composition to class inheritance” Gang of 4

Borrowing methods pattern

•  call() and apply() 

notmyobj.doStuff.call(myobj, param1, p2, p3) notmyobj.doStuff.apply(myobj, [param1, p2, p3]) 

Example: borrowing from Array function f() {    var args = [].slice.call(arguments, 1, 3);    return args; } 

>>> f(1, 2, 3, 4, 5, 6) 2,3 

[].slice.call  can also be Array.prototype.slice.call 

Inheritance by copying properties

function extend(parent, child) {   var i, child = child || {};   for (i in parent) {     child[i] = parent[i];   }   return child; } 

Mixins function mixInThese() {   var arg, prop, child = {};   for (arg = 0; arg < arguments.length; arg++) {     for (prop in arguments[arg]) {       child[prop] = arguments[arg][prop];     }   }   return child; } 

var cake = mixInThese(  {eggs: 2, large: true},   {butter: 1, salted: true},   {flour: “3 cups”},  {sugar: “sure!”} ); 

Classical inheritance

function Parent(){   this.name = 'Adam'; } Parent.prototype.say = function(){   return this.name; }; 

function Child(){} 

inherit(Child, Parent); 

Option 1

function inherit(C, P) {   C.prototype = new P(); } 

ECMA standard

Option 2 – rent-a-constructor

function C(a, c, b, d) {   P.call(this, arguments); } •  Advantage – passes arguments when creating an object •  Drawback – won’t inherit from the prototype

Option 3 – rent + prototype

function C(a, c, b, d) {   P.call(this, arguments); } C.prototype = new P(); 

Option 4

function inherit(C, P) {   C.prototype = P.prototype; } •  Advantage: everybody shares the same prototype •  Drawback: everybody shares the same prototype 

Option 5

function inherit(C, P) {   var F = function(){};   F.prototype = P.prototype;   C.prototype = new F(); } •  Only inherits properties/methods of the prototype

Option 5 + super

function inherit(C, P) { 

  var F = function(){}; 

  F.prototype = P.prototype; 

  C.prototype = new F(); 

  C.uber = P.prototype; 

Option 5 + super + constructor

reset function inherit(C, P) {   var F = function(){};   F.prototype = P.prototype;   C.prototype = new F();   C.uber = P.prototype;   C.prototype.constructor = C; } 

Prototypal inheritance

•  by Douglas Crockford •  no class-like constructors •  objects inherit from objects •  via the prototype

Prototypal inheritance

function object(o) {   function F(){}   F.prototype = o;   return new F(); }  ES5

FTW

Prototypal inheritance

>>> var parent = {a: 1}; 

>>> var child = object(parent); 

>>> child.a; 

>>> child.hasOwnProperty(“a”); 

false 

Functions

Functions are objects

Self-executable functions

(function(){    var a = 1;    var b = 2;    alert(a + b); })(); 

Self-executable functions

(function(a, b){   var c = a + b;   alert(c); })(1, 2); 

Callbacks

function test(a, b, fn) {     fn(a, b); } 

test(1, 2, myFunc); 

test(1, 2, function(one, two){     console.log(arguments); }); 

Callback pattern example

document.addEventListener(  'click',   animateAndWowUser,   false 

); 

Callback pattern example

var thePlotThickens = function(){   console.log('500ms later...'); }; 

setTimeout(thePlotThickens, 500); 

// anti‐pattern setTimeout("thePlotThickens()", 500); 

Returning functions function setup() {     alert(1);     return function() {         alert(2);     }; } var my = setup(); // alerts 1 my(); // alerts 2 

Returning functions function setup() {     var count = 0;     return function() {         return ++count;     }; } var next = setup(); next(); // 1 next(); // 2 

Self-overwriting functions function next() {     var count = 1;     next = function() {         return ++count;     };     return count; } next(); // 1 next(); // 2 

Lazy function definition function lazy() {     var result = 2 + 2;     lazy = function() {         return result;     };     return lazy(); } lazy(); // 4 lazy(); // 4 

Function properties function myFunc(param){     if (!myFunc.cache) {         myFunc.cache = {};     }     if (!myFunc.cache[param]) {         var result = {}; // …         myFunc.cache[param] = result;     }     return myFunc.cache[param]; } 

Init-time branching // BEFORE var addListener = function(el, type, fn) {  

  // w3c   if (typeof window.addEventListener === 'function') {      el.addEventListener(type, fn, false);  

  // IE   } else if (typeof document.attachEvent === 'function') {     el.attachEvent('on' + type, fn);  

  // older browsers   } else {       el['on' + type] = fn;    }  };  

Init-time branching var addListener;  

if (typeof window.addEventListener === 'function') {    addListener = function(el, type, fn) {      el.addEventListener(type, fn, false);    };  } else if (typeof document.attachEvent === 'function') {   addListener = function(el, type, fn) {      el.attachEvent('on' + type, fn);   };  } else {   addListener = function(el, type, fn) {        el['on' + type] = fn;   };  }  

More object creation patterns

Private/privileged function MyConstr() {     var a = 1;     this.sayAh = function() {         return a;     }; } 

>>> new MyConstr().sayAh(); 1 

Declaring dependencies YAHOO.properties.foo = function() {   var Event = YAHOO.util.Event,       Dom   = YAHOO.util.Dom; 

  // ... 

}(); 

Namespacing

var myApp = {}; myApp.someObj = {}; myApp.someObj.someFunc =                function(){}; 

Namespacing

function namespace(){…} 

>>> namespace(‘my.name.space’) >>> typeof my.name.space “object” 

Chaining var o = {   v: 1,   increment: function() {     this.v++;     return this;   },   add: function(v) {     this.v += v;     return this;   },   shout: function(){     alert(this.v);   } }; 

>>> o.increment().add(3).shout() // 5 

Configuration objects myPerson(last, first, dob, address) 

vs.

var conf = {     first: 'Bruce',     last: 'Wayne' }; myPerson(conf); 

Static members – public

function MyMath() {   // math here... } 

MyMath.PI = 3.14; MyMath.E  = 2.7; 

Module pattern MYAPP.namespace('modules.foo'); MYAPP.modules.foo = function() {   var a = 1;   return {     getA: function(){       return a;     }   }; }(); 

Design Patterns

Singleton

var mysingleton = {}; 

•  It’s as simple as that

Singleton v.2 (classical)

var my1 = new Single(); var my2 = new Single(); >>> my1 === my2 true 

•  How to make this work?

Singleton v.2 - option 1

var Single = function() {   if (typeof Single.instance === “object”) {     return Single.instance;   }   Single.instance = this; }; 

•  Drawback – the instance property is public

… option 2 (closure)

function Single() { 

  var instance = this;  

  // add more to this… 

  Single = function (){     return instance;   }; } 

Factory

var Polygon = function() {}; var triangle = Polygon.factory(‘Triangle’); var circle   = Polygon.factory(‘Circle’); 

Polygon.Triangle = function(){}; Polygon.Circle   = function(){}; 

Polygon.factory = function(name) {   if (typeof Polygon[name] === “function”) {     return new Polygon[name]();    } }; 

And one more thing…

Run JSLint

•  http://jslint.com •  Integrate with your editor

JSLint

•  missing semi-colons •  missing curly brackets •  undeclared vars •  trailing commas •  unreachable code •  for-in loops •  …

Thank you!

More…

http://jspatterns.com http://slideshare.net/stoyan http://jsmag.com column

top related