Top Banner
JavaScript Patterns Stoyan Stefanov, Yahoo! @stoyanstefanov Ajax Experience, Boston 2009 3:25 p.m. Sept 15, 2009
71

JavaScript Patterns

Sep 08, 2014

Download

Technology

Stoyan Stefanov

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
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: JavaScript Patterns

JavaScript Patterns Stoyan Stefanov, Yahoo! @stoyanstefanov

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

Page 2: JavaScript Patterns

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

Page 3: JavaScript Patterns

Types of patterns

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

Page 4: JavaScript Patterns
Page 5: JavaScript Patterns

In this session…

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

Page 6: JavaScript Patterns

Object creation patterns

Page 7: JavaScript Patterns

#

Page 8: JavaScript Patterns

Two ways to create objects

•  Object literal •  Constructor functions

{} vs. new

Page 9: JavaScript Patterns

Object literal

var adam = {}; // clean slate 

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

Page 10: JavaScript Patterns

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

Page 11: JavaScript Patterns

Using a constructor

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

Page 12: JavaScript Patterns

Constructor functions var Person = function(name) { 

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

}; 

Page 13: JavaScript Patterns

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

Page 14: JavaScript Patterns

Naming convention

MyConstructor myFunction 

Page 15: JavaScript Patterns

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

  that.name = name; 

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

  }; 

  return that;  }  

Page 16: JavaScript Patterns

Enforcing new 

this instanceof Person 

this instanceof arguments.callee 

ES5 FTW

Page 17: JavaScript Patterns

Prototype

var Person = function(name) { 

  this.name = name; 

}; 

Person.prototype.say = function() { 

  return “I am ” + this.name; 

}; 

Page 18: JavaScript Patterns

How is the prototype used?

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

Page 19: JavaScript Patterns

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

Page 20: JavaScript Patterns

Code reuse patterns

Page 21: JavaScript Patterns

Inheritance – friend or foe

“Prefer object composition to class inheritance” Gang of 4

Page 22: JavaScript Patterns
Page 23: JavaScript Patterns

Borrowing methods pattern

•  call() and apply() 

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

Page 24: JavaScript Patterns

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 

Page 25: JavaScript Patterns

Inheritance by copying properties

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

Page 26: JavaScript Patterns

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; } 

Page 27: JavaScript Patterns

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

Page 28: JavaScript Patterns

Classical inheritance

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

function Child(){} 

inherit(Child, Parent); 

Page 29: JavaScript Patterns

Option 1

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

ECMA standard

Page 30: JavaScript Patterns

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

Page 31: JavaScript Patterns

Option 3 – rent + prototype

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

Page 32: JavaScript Patterns

Option 4

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

Page 33: JavaScript Patterns

Option 5

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

Page 34: JavaScript Patterns

Option 5 + super

function inherit(C, P) { 

  var F = function(){}; 

  F.prototype = P.prototype; 

  C.prototype = new F(); 

  C.uber = P.prototype; 

Page 35: JavaScript Patterns

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; } 

Page 36: JavaScript Patterns

Prototypal inheritance

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

Page 37: JavaScript Patterns

Prototypal inheritance

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

FTW

Page 38: JavaScript Patterns

Prototypal inheritance

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

>>> var child = object(parent); 

>>> child.a; 

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

false 

Page 39: JavaScript Patterns

Functions

Page 40: JavaScript Patterns

Functions are objects

Page 41: JavaScript Patterns

Self-executable functions

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

Page 42: JavaScript Patterns

Self-executable functions

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

Page 43: JavaScript Patterns

Callbacks

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

test(1, 2, myFunc); 

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

Page 44: JavaScript Patterns

Callback pattern example

document.addEventListener(  'click',   animateAndWowUser,   false 

); 

Page 45: JavaScript Patterns

Callback pattern example

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

setTimeout(thePlotThickens, 500); 

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

Page 46: JavaScript Patterns

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

Page 47: JavaScript Patterns

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

Page 48: JavaScript Patterns

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

Page 49: JavaScript Patterns

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

Page 50: JavaScript Patterns

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

Page 51: JavaScript Patterns

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;    }  };  

Page 52: JavaScript Patterns

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;   };  }  

Page 53: JavaScript Patterns

More object creation patterns

Page 54: JavaScript Patterns

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

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

Page 55: JavaScript Patterns

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

  // ... 

}(); 

Page 56: JavaScript Patterns

Namespacing

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

Page 57: JavaScript Patterns

Namespacing

function namespace(){…} 

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

Page 58: JavaScript Patterns

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 

Page 59: JavaScript Patterns

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

vs.

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

Page 60: JavaScript Patterns

Static members – public

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

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

Page 61: JavaScript Patterns

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

Page 62: JavaScript Patterns

Design Patterns

Page 63: JavaScript Patterns

Singleton

var mysingleton = {}; 

•  It’s as simple as that

Page 64: JavaScript Patterns

Singleton v.2 (classical)

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

•  How to make this work?

Page 65: JavaScript Patterns

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

Page 66: JavaScript Patterns

… option 2 (closure)

function Single() { 

  var instance = this;  

  // add more to this… 

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

Page 67: JavaScript Patterns

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]();    } }; 

Page 68: JavaScript Patterns

And one more thing…

Page 69: JavaScript Patterns

Run JSLint

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

Page 70: JavaScript Patterns

JSLint

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

Page 71: JavaScript Patterns

Thank you!

More…

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