0
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, article...
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
•  D...
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.nam...
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 ” +...
Constructor functions
var Person = function(name) { 
  // var this = {}; 
  this.name = name; 
  this.say = function() { 
...
Naming convention

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


  that.name = name; 
  that.say = func...
Enforcing new 
this instanceof Person 

this instanceof arguments.callee 



                           ES5
              ...
Prototype
var Person = function(name) { 
  this.name = name; 
}; 
Person.prototype.say = function() { 
  return “I am ” + ...
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(my...
Example: borrowing from Array
function f() { 
   var args = [].slice.call(arguments, 1, 3); 
   return args; 
} 

>>> f(1,...
Inheritance by copying properties
function extend(parent, child) { 
  var i, child = child || {}; 
  for (i in parent) { 
...
Mixins
function mixInThese() { 
  var arg, prop, child = {}; 
  for (arg = 0; arg < arguments.length; arg++) { 
    for (p...
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...
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
whe...
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
•  D...
Option 5
function inherit(C, P) { 
  var F = function(){}; 
  F.prototype = P.prototype; 
  C.prototype = new F(); 
} 
•  ...
Option 5 + super

function inherit(C, P) { 
  var F = function(){}; 
  F.prototype = P.prototype; 
  C.prototype = new F()...
Option 5 + super + constructor
reset
function inherit(C, P) { 
  var F = function(){}; 
  F.prototype = P.prototype; 
  C....
Prototypal inheritance

•  by Douglas Crockford
•  no class-like constructors
•  objects inherit from objects
•  via the p...
Prototypal inheritance
function object(o) { 
  function F(){} 
  F.prototype = o; 
  return new F(); 
                    ...
Prototypal inheritance

>>> var parent = {a: 1}; 
>>> var child = object(parent); 
>>> child.a; 
1 
>>> child.hasOwnProper...
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....
Callback pattern example

document.addEventListener( 
   'click',  
   animateAndWowUser,  
   false 
); 
Callback pattern example
var thePlotThickens = function(){ 
  console.log('500ms later...'); 
}; 
setTimeout(thePlotThicke...
Returning functions
function setup() { 
    alert(1); 
    return function() { 
        alert(2); 
    }; 
} 
var my = set...
Returning functions
function setup() { 
    var count = 0; 
    return function() { 
        return ++count; 
    }; 
} 
v...
Self-overwriting functions
function next() { 
    var count = 1; 
    next = function() { 
        return ++count; 
    };...
Lazy function definition
function lazy() { 
    var result = 2 + 2; 
    lazy = function() { 
        return result; 
    ...
Function properties
function myFunc(param){ 
    if (!myFunc.cache) { 
        myFunc.cache = {}; 
    } 
    if (!myFunc....
Init-time branching
// BEFORE 
var addListener = function(el, type, fn) {  

  // w3c 
  if (typeof window.addEventListene...
Init-time branching
var addListener;  

if (typeof window.addEventListener === 'function') {  
  addListener = function(el...
More object creation patterns
Private/privileged
function MyConstr() { 
    var a = 1; 
    this.sayAh = function() { 
        return a; 
    }; 
} 

>>...
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.sp...
Chaining
var o = { 
  v: 1, 
  increment: function() { 
    this.v++; 
    return this; 
  }, 
  add: function(v) { 
    t...
Configuration objects
myPerson(last, first, dob, 
address) 

vs.

var conf = { 
    first: 'Bruce', 
    last: 'Wayne' 
};...
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: func...
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 wo...
Singleton v.2 - option 1
var Single = function() { 
  if (typeof Single.instance === “object”) { 
    return Single.instan...
… option 2 (closure)
function Single() { 

  var instance = this;  

  // add more to this… 

  Single = function (){ 
   ...
Factory
var Polygon = function() {}; 
var triangle = Polygon.factory(‘Triangle’); 
var circle   = Polygon.factory(‘Circle’...
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-i...
Thank you!

More…
http://jspatterns.com
http://slideshare.net/stoyan
http://jsmag.com column
Javascript patterns
Javascript patterns
Upcoming SlideShare
Loading in...5
×

Javascript patterns

2,430

Published on

javascript design patterns oop inheritance

0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,430
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
91
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

Transcript of "Javascript patterns"

  1. 1. JavaScript Patterns Stoyan Stefanov, Yahoo! @stoyanstefanov Ajax Experience, Boston 2009 3:25 p.m. Sept 15, 2009
  2. 2. About me •  Yahoo! Search •  Yahoo! Exceptional Performance •  YSlow 2.0 architect •  http://smush.it •  Books, articles •  http://phpied.com
  3. 3. Types of patterns •  OO Design Patterns (gang of four) •  JavaScript-specific coding patterns •  Anti-patterns
  4. 4. In this session… •  Object creation patterns •  Code reuse patterns •  Functional patterns •  More on object creation •  Design patterns
  5. 5. Object creation patterns
  6. 6. #
  7. 7. Two ways to create objects •  Object literal •  Constructor functions {} vs. new
  8. 8. Object literal var adam = {}; // clean  slate  adam.name = “Adam”;  adam.say = function() {    return “I am ” +  this.name;  }; 
  9. 9. Object literal var adam = {    name: “Adam”,    say: function() {      return “I am ” + this.name;    }  }; 
  10. 10. Using a constructor var adam = new Person(“Adam”);  adam.say(); // “I am Adam” 
  11. 11. Constructor functions var Person = function(name) {    this.name = name;    this.say = function() {      return “I am ” + this.name;    };  }; 
  12. 12. Constructor functions var Person = function(name) {    // var this = {};    this.name = name;    this.say = function() {      return “I am ” + this.name;    };    // return this;  }; 
  13. 13. Naming convention MyConstructor  myFunction 
  14. 14. Enforcing new  function Person() {     var that = (this === window) ? {} : this;    that.name = name;    that.say = function() {      return “I am ” + that.name;    };    return that;   }  
  15. 15. Enforcing new  this instanceof Person  this instanceof arguments.callee  ES5 FTW
  16. 16. Prototype var Person = function(name) {    this.name = name;  };  Person.prototype.say = function() {    return “I am ” + this.name;  }; 
  17. 17. How is the prototype used? >>> var adam = new Person('Adam');  >>> adam.name;  "Adam"  >>> adam.say();  "I am Adam" 
  18. 18. If you want to reuse it, add it to the prototype
  19. 19. Code reuse patterns
  20. 20. Inheritance – friend or foe “Prefer object composition to class inheritance” Gang of 4
  21. 21. Borrowing methods pattern •  call() and apply()  notmyobj.doStuff.call(myobj, param1, p2, p3)  notmyobj.doStuff.apply(myobj, [param1, p2, p3])  
  22. 22. 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 
  23. 23. Inheritance by copying properties function extend(parent, child) {    var i, child = child || {};    for (i in parent) {      child[i] = parent[i];    }    return child;  } 
  24. 24. 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;  } 
  25. 25. var cake = mixInThese(   {eggs: 2, large: true},    {butter: 1, salted: true},   {flour: “3 cups”},   {sugar: “sure!”}  ); 
  26. 26. Classical inheritance function Parent(){    this.name = 'Adam';  }  Parent.prototype.say = function(){    return this.name;  };  function Child(){}  inherit(Child, Parent); 
  27. 27. Option 1 function inherit(C, P) {    C.prototype = new P();  }  ECMA standard
  28. 28. 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
  29. 29. Option 3 – rent + prototype function C(a, c, b, d) {    P.call(this, arguments);  }  C.prototype = new P(); 
  30. 30. Option 4 function inherit(C, P) {    C.prototype = P.prototype;  }  •  Advantage: everybody shares the same prototype •  Drawback: everybody shares the same prototype 
  31. 31. Option 5 function inherit(C, P) {    var F = function(){};    F.prototype = P.prototype;    C.prototype = new F();  }  •  Only inherits properties/ methods of the prototype
  32. 32. Option 5 + super function inherit(C, P) {    var F = function(){};    F.prototype = P.prototype;    C.prototype = new F();    C.uber = P.prototype;  } 
  33. 33. 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;  } 
  34. 34. Prototypal inheritance •  by Douglas Crockford •  no class-like constructors •  objects inherit from objects •  via the prototype
  35. 35. Prototypal inheritance function object(o) {    function F(){}    F.prototype = o;    return new F();  ES5 }  FTW
  36. 36. Prototypal inheritance >>> var parent = {a: 1};  >>> var child = object(parent);  >>> child.a;  1  >>> child.hasOwnProperty(“a”);  false 
  37. 37. Functions
  38. 38. Functions are objects
  39. 39. Self-executable functions (function(){     var a = 1;     var b = 2;     alert(a + b);  })(); 
  40. 40. Self-executable functions (function(a, b){    var c = a + b;    alert(c);  })(1, 2); 
  41. 41. Callbacks function test(a, b, fn) {      fn(a, b);  }  test(1, 2, myFunc);  test(1, 2, function(one, two){      console.log(arguments);  }); 
  42. 42. Callback pattern example document.addEventListener(   'click',    animateAndWowUser,    false  ); 
  43. 43. Callback pattern example var thePlotThickens = function(){    console.log('500ms later...');  };  setTimeout(thePlotThickens, 500);  // anti‐pattern  setTimeout("thePlotThickens()",  500); 
  44. 44. Returning functions function setup() {      alert(1);      return function() {          alert(2);      };  }  var my = setup(); // alerts 1   my(); // alerts 2 
  45. 45. Returning functions function setup() {      var count = 0;      return function() {          return ++count;      };  }  var next = setup();  next(); // 1  next(); // 2 
  46. 46. Self-overwriting functions function next() {      var count = 1;      next = function() {          return ++count;      };      return count;  }  next(); // 1  next(); // 2 
  47. 47. Lazy function definition function lazy() {      var result = 2 + 2;      lazy = function() {          return result;      };      return lazy();  }  lazy(); // 4  lazy(); // 4 
  48. 48. Function properties function myFunc(param){      if (!myFunc.cache) {          myFunc.cache = {};      }      if (!myFunc.cache[param]) {          var result = {}; // …          myFunc.cache[param] = result;      }      return myFunc.cache[param];  } 
  49. 49. 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;     }   };  
  50. 50. 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;    };   }  
  51. 51. More object creation patterns
  52. 52. Private/privileged function MyConstr() {      var a = 1;      this.sayAh = function() {          return a;      };  }  >>> new MyConstr().sayAh();  1 
  53. 53. Declaring dependencies YAHOO.properties.foo = function()  {    var Event = YAHOO.util.Event,        Dom   = YAHOO.util.Dom;    // ...  }(); 
  54. 54. Namespacing var myApp = {};  myApp.someObj = {};  myApp.someObj.someFunc =                 function() {}; 
  55. 55. Namespacing function namespace(){…}  >>> namespace(‘my.name.space’)   >>> typeof my.name.space  “object” 
  56. 56. 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 
  57. 57. Configuration objects myPerson(last, first, dob,  address)  vs. var conf = {      first: 'Bruce',      last: 'Wayne'  };  myPerson(conf); 
  58. 58. Static members – public function MyMath() {    // math here...  }  MyMath.PI = 3.14;  MyMath.E  = 2.7; 
  59. 59. Module pattern MYAPP.namespace('modules.foo');  MYAPP.modules.foo = function() {    var a = 1;    return {      getA: function(){        return a;      }    };  }(); 
  60. 60. Design Patterns
  61. 61. Singleton var mysingleton = {};  •  It’s as simple as that
  62. 62. Singleton v.2 (classical) var my1 = new Single();  var my2 = new Single();  >>> my1 === my2  true  •  How to make this work?
  63. 63. 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
  64. 64. … option 2 (closure) function Single() {    var instance = this;     // add more to this…    Single = function (){      return instance;    };  } 
  65. 65. 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]();     }  }; 
  66. 66. And one more thing…
  67. 67. Run JSLint •  http://jslint.com •  Integrate with your editor
  68. 68. JSLint •  missing semi-colons •  missing curly brackets •  undeclared vars •  trailing commas •  unreachable code •  for-in loops •  …
  69. 69. Thank you! More… http://jspatterns.com http://slideshare.net/stoyan http://jsmag.com column
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×