YOU ARE DOWNLOADING DOCUMENT

Please tick the box to continue:

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


Related Documents