Javascript patterns

Preview:

DESCRIPTION

The presentation covers Javascript Gotcha's and patterns to use while implementing client side browser applications.

Citation preview

JavaScript  -­‐  What’s  in  the  language?  

JavaScript  as  a  language  •  Implementa3on  of  ECMASCRIPT  •  Object  oriented  •  No  Classes  •  Dynamic  Objects  -­‐  collec3on  of  named  proper3es.  You  can  add,  remove,  update  its  members.    

•  Loose  typing  •  lambdas  •  Na3ve  and  host  objects  -­‐  Environment  specific  •  Prototypical  inheritance  

Good  Parts  

•  Lambda  •  Dynamic  objects  •  Loose  typing  •  Object  literals  

Bad  Parts  

•  Global  Variables  •  +  adds  and  concatenates  •  Semicolon  inser3on  •  typeof  •  with  and  eval  •  ==  and  !=  •  false,  null,  undefined,  NaN  

General  Pa8erns  

Minimizing  global  variables  

/*#Title:#Minimizing#Globals#Description:#they#are#declared#outside#of#any#function#or#simply#used#without#being#declared!

# # # */!!! ! ! myglobal!=!"hello";!//#antipattern!! ! ! console.log(myglobal);!//#"hello"!! ! ! console.log(window.myglobal);!//#"hello"!! ! ! console.log(window["myglobal"]);!//#"hello"!! ! ! console.log(this.myglobal);!//#"hello"!!

Implied  global  variables  

! ! ! //!antipatten!1!! ! ! function!sum(x,!y)!{!! ! ! ! //!implied!global!! ! ! ! result!=!x!+!y;!! ! ! ! return!result;!! ! ! }!!!! ! ! //!preferred!1!! ! ! function!sum(x,!y)!{!

//!a!variable!declared!inside!of!a!function!is!local!to!that!function!and!not!available!outside!the!function!

! ! ! ! var!result!=!x!+!y;!! ! ! ! return!result;!! ! ! }!!!!

! ! ! //"antipattern"2!! ! ! function!foo()!{!! ! ! ! var!a!=!b!=!0;!! ! ! ! //"...!! ! ! }!!! ! ! //"the"preceding"code"snippet"will"behave"as"if"you've"typed"the"following!! ! ! var!a!=!(b!=!0);!!!! ! ! //"preferred"2!! ! ! function!foo()!{!! ! ! ! var!a,!b;!! ! ! ! //"...!! ! ! ! a!=!b!=!0;!//"both"local!! ! ! }!!

Single  var  PaSern  ! ! !!! ! ! /*!Benefits:!

!*!1.!Provides!a!single!place!to!look!for!all!the!local!variables!needed!by!the!function!

! ! ! !*!2.!Prevents!logical!errors!when!a!variable!is!used!before!it's!defined!! ! ! !*!3.!Helps!you!remember!to!declare!variables!and!therefore!minimize!globals!! ! ! */!!! ! ! function!func()!{!! ! ! ! var!a!=!1,!! ! ! ! ! ! b!=!2,!! ! ! ! ! ! sum!=!a!+!b,!! ! ! ! ! ! myobject!=!{},!! ! ! ! ! ! i,!! ! ! ! ! ! j;!!! ! ! ! //!function!body...!! ! ! }!!! ! ! function!updateElement()!{!! ! ! ! var!el!=!document.getElementById("result"),!! ! ! ! ! ! style!=!el.style;!! ! ! ! //!do!something!with!el!and!style...!! ! ! }!!

Namespacing  

! ! ! //!unsafe!! ! ! var!MYAPP!=!{};!! ! ! //!better!! ! ! if!(typeof!MYAPP!===!"undefined")!{!! ! ! ! var!MYAPP!=!{};!! ! ! }!! ! ! //!or!shorter!! ! ! var!MYAPP!=!MYAPP!||!{};!!

! ! ! MYAPP.namespace!=!function!(ns_string)!{!! ! ! ! var!parts!=!ns_string.split('.'),!! ! ! ! ! ! parent!=!MYAPP,!! ! ! ! ! ! i;!!! ! ! ! //"strip"redundant"leading"global!! ! ! ! if!(parts[0]!===!"MYAPP")!{!! ! ! ! ! parts!=!parts.slice(1);!! ! ! ! }!!! ! ! ! for!(i!=!0;!i!<!parts.length;!i!+=!1)!{!! ! ! ! ! //"create"a"property"if"it"doesn't"exist!! ! ! ! ! if!(typeof!parent[parts[i]]!===!"undefined")!{!! ! ! ! ! ! parent[parts[i]]!=!{};!! ! ! ! ! }!! ! ! ! ! parent!=!parent[parts[i]];!! ! ! ! }!! ! ! ! return!parent;!! ! ! };!!! ! ! //"assign"returned"value"to"a"local"var!! ! ! var!module2!=!MYAPP.namespace('MYAPP.modules.module2');!! ! ! console.log(module2!===!MYAPP.modules.module2);!//"true!!!

Implied  Typecas3ng:  false,  null,  undefined,  NaN  

! ! ! var!zero!=!0;!!! ! ! /*#antipattern!# # # #*#JavaScript#implicitly#typecasts#variables#when#you#compare#them.!# # # #*#That's#why#comparisons#such#as#false#==#0#or#""#==#0#return#true.!# # # #*/!! ! ! if!(zero!==!false)!{!! ! ! ! //#this#block#is#executed...!! ! ! }!!!! ! ! //#preferred!! ! ! /*#To#avoid#confusion#caused#by#the#implied#typecasting,!

*#always#use#the#===#and#!==#operators#that#check#both#the#values#and#the#type#of#the#expressions#you#compare!

# # # */!! ! ! if!(zero!===!false)!{!! ! ! ! //#not#executing#because#zero#is#0,#not#false!! ! ! }!! !

Avoiding  eval  

! ! ! /*!Title:!Avoiding!eval()!! ! ! !*!Description:!avoid!using!eval()!! ! ! !*/!!! ! ! //!antipattern!1!! ! ! var!property!=!"name";!! ! ! alert(eval("obj."!+!property));!!!! ! ! //!preferred!1!! ! ! var!property!=!"name";!! ! ! alert(obj[property]);!!!

! ! ! /*#antipattern#2!*#It's#also#important#to#remember#that#passing#strings#to#setInterval(),#setTimeout(),!*#and#the#Function()#constructor#is,#for#the#most#part,#similar#to#using#eval()#and#therefore#should#be#avoided.!

# # # */!! ! ! setTimeout("myFunc()",!1000);!! ! ! setTimeout("myFunc(1,!2,!3)",!1000);!!!! ! ! //#preferred#2!! ! ! setTimeout(myFunc,!1000);!! ! ! setTimeout(function!()!{!! ! ! ! myFunc(1,!2,!3);!! ! ! },!1000);!! ! ! //#in#supported#browsers#(i.e.#not#IE)!! ! ! setTimeout(myFunc,!1000,!1,!2,!3);!!!

For-­‐in  loops  ! ! ! /*#Title:#for-in#loops!# # # #*#Description:#optimized#for-in#loops!# # # #*/!!! ! ! //#the#object!! ! ! var!man!=!{!! ! ! ! hands:2,!! ! ! ! legs:2,!! ! ! ! heads:1!! ! ! };!!! ! ! //#somewhere#else#in#the#code!! ! ! //#a#method#was#added#to#all#objects!! ! ! if!(typeof!Object.prototype.clone!===!'undefined')!{!! ! ! ! Object.prototype.clone!=!function!()!{!! ! ! ! };!! ! ! }!!! !

! ! ! //"antipattern!! ! ! //"for,in"loop"without"checking"hasOwnProperty()!! ! ! for!(var!i!in!man)!{!! ! ! ! console.log(i,!":",!man[i]);!! ! ! }!! ! ! /*!" " " "*"result"in"the"console!" " " "*"hands":"2!" " " "*"legs":"2!" " " "*"hands":"1!" " " "*"clone:"function()!" " " "*/!!!! ! ! //"preferred"1!! ! ! for!(var!i!in!man)!{!! ! ! ! if!(man.hasOwnProperty(i))!{!//"filter!! ! ! ! ! console.log(i,!":",!man[i]);!! ! ! ! }!! ! ! }!!! ! ! /*!" " " "*"result"in"the"console!" " " "*"hands":"2!" " " "*"legs":"2!" " " "*"heads":"1!" " " "*/!!

Automa3c  semicolon  inser3on  

//antipattern!return!{!!!!!status:!true!};!!//preferred!return{!!!!!status:!true!};!!

Func:on  Pa8erns  

Closures  //"Your"setup"function"can"store"some"private"data"in"a"closure"and"use"that"data"somehow.!//"Here"setup()"creates"a"counter"function,"which"gives"a"next"ID"for"example."But"the"count"variable"is"not"exposed.!

!! ! ! var!setup!=!function!()!{!! ! ! ! var!count!=!0;!! ! ! ! return!function!()!{!! ! ! ! ! return!++count;!! ! ! ! };!! ! ! };!! ! ! //"usage!! ! ! var!next!=!setup();!! ! ! //next();"//"returns"1!! ! ! //next();"//"returns"2!! ! ! //next();"//"returns"3!!

Currying  ! ! ! /***!# # # #currying!# # # #***/!!! ! ! //#a#curried#add()!! ! ! //#accepts#partial#list#of#arguments!! ! ! function!add(x,!y)!{!! ! ! ! var!oldx!=!x,!oldy!=!y;!! ! ! ! if!(typeof!oldy!===!"undefined")!{!//#partial!! ! ! ! ! return!function!(newy)!{!! ! ! ! ! ! return!oldx!+!newy;!! ! ! ! ! }!! ! ! ! }!! ! ! ! //#full#application!! ! ! ! return!x!+!y;!! ! ! }!!! ! ! //#test!! ! ! typeof!add(5);!//#"function"!! ! ! add(3)(4);!//#7!!

Literals,  Constructors  and  Object  crea:on  Pa8erns  

To  ‘new’  or  not  ! ! ! ! !!! ! ! //!antipattern!! ! ! var!car!=!new!Object();!! ! ! car.goes!=!"far";!!!! ! ! //!preferred!! ! ! var!car!=!{goes:"far"};!!! ! ! //!constructor!! ! ! function!Waffle()!{!! ! ! ! this.tastes!=!"yummy";!! ! ! }!!! ! ! //!antipattern!! ! ! //!forgotten!`new`!! ! ! var!good_morning!=!Waffle();!! ! ! console.log(typeof!good_morning);!//!"undefined"!! ! ! console.log(window.tastes);!//!"yummy"!!!! ! ! //!preferred!! ! ! var!good_morning!=!new!Waffle();!! ! ! console.log(typeof!good_morning);!//!"object"!! ! ! console.log(good_morning.tastes);!//!"yummy"!!!! ! !!

PaSerns  for  enforcing  ‘new’  ! ! ! !! ! ! function!Waffle()!{!! ! ! ! if!(!(this!instanceof!Waffle))!{!! ! ! ! ! return!new!Waffle();!! ! ! ! }!!! ! ! ! this.tastes!=!"yummy";!! ! ! }!!! ! ! var!good_morning!=!new!Waffle();!! ! ! var!good_evening!=!Waffle();!!! ! ! console.log(typeof!good_morning);!//""object"!! ! ! console.log(good_morning.tastes);!//""yummy"!! ! ! console.log(typeof!good_evening);!//""object"!! ! ! console.log(good_evening.tastes);!//""yummy"!!

! ! ! //!antipattern!! ! ! //!with!wrappers!! ! ! var!s!=!new!String("my!string");!! ! ! var!n!=!new!Number(101);!! ! ! var!b!=!new!Boolean(true);!!!! ! ! //!preferred!! ! ! //!without!wrappers!! ! ! var!s!=!"my!string";!! ! ! var!n!=!101;!! ! ! var!b!=!true;!!!

! ! ! /*!# # # #only#use#primitive#wrappers#when#you#want#to#augment#the#value#and#persist#state!# # # #*/!!! ! ! //#primitive#string!! ! ! var!greet!=!"Hello!there";!! ! ! //#primitive#is#converted#to#an#object!! ! ! //#in#order#to#use#the#split()#method!! ! ! greet.split('!')[0];!//#"Hello"!! ! ! //#attemting#to#augment#a#primitive#is#not#an#error!! ! ! greet.smile!=!true;!! ! ! //#but#it#doesn't#actually#work!! ! ! console.log(typeof!greet.smile);!//#"undefined"!!! ! ! //#primitive#wrapper!! ! ! var!greet!=!new!String("Hello!there");!! ! ! //#split()#method#is#called#directly#on#the#object!! ! ! greet.split('!')[0];!//#"Hello"!! ! ! //#augment#the#object!! ! ! greet.smile!=!true;!! ! ! console.log(typeof!greet.smile);!//#"boolean"!!

Module  paSern  MYAPP.utilities.array/=/(function/()/{// / / / //"private"properties// / / / var/array_string/=/"[object/Array]",// / / / ops/=/Object.prototype.toString,/// / / / //"private"methods// / / / inArray/=/function/(haystack,/needle)/{// / / / / for/(var/i/=/0,/max/=/haystack.length;/i/</max;/i/+=/1)/{// / / / / / if/(haystack[i]/===/needle)/{// / / / / / / return/i;// / / / / / }// / / / / }// / / / / return//1;// / / / },// / / / isArray/=/function/(a)/{// / / / / return/ops.call(a)/===/array_string;// / / / };// / / / //"end"var/// / / / //"revealing"public"API// / / / return/{// / / / / isArray:isArray,// / / / / indexOf:inArray// / / / };// / / }());/ !

Code  Reuse  Pa8erns  

Prototypal  Inheritance  

! ! ! function!object(o)!{!! ! ! ! function!F()!{!! ! ! ! }!!! ! ! ! F.prototype!=!o;!! ! ! ! return!new!F();!! ! ! }!!! ! ! //"object"to"inherit"from!! ! ! var!parent!=!{!! ! ! ! name:"Papa"!! ! ! };!!! ! ! //"the"new"object!! ! ! var!child!=!object(parent);!!! ! ! //"testing!! ! ! console.log(child.name);!//""Papa"!!! !

! ! ! //"parent"constructor!! ! ! function!Person()!{!! ! ! ! //"an""own""property!! ! ! ! this.name!=!"Adam";!! ! ! }!! ! ! //"a"property"added"to"the"prototype!! ! ! Person.prototype.getName!=!function!()!{!! ! ! ! return!this.name;!! ! ! };!! ! ! //"create"a"new"person!! ! ! var!papa!=!new!Person();!! ! ! //"inherit!! ! ! var!kid!=!object(papa);!! ! ! //"test"that"both"the"own"property!! ! ! //"and"the"prototype"property"were"inherited!! ! ! console.log(kid.getName());!//""Adam"!!

Augmen3ng  built-­‐in  Prototypes  

! ! ! if!(typeof!Object.prototype.myMethod!!==!"function")!{!! ! ! ! Object.prototype.myMethod!=!function!()!{!! ! ! ! ! //"implementation...!! ! ! ! };!! ! ! }!!

Design  Pa8erns  

CREATIONAL  

Builder   constructs  complex  objects  by  separa3ng  construc3on  and  representa3on  

Factory  Method   creates  objects  without  specifying  the  exact  class  to  create  

Singleton   restricts  object  crea3on  for  a  class  to  only  one  instance  

STRUCTURAL  

Decorator   dynamically  adds/overrides  behavior  in  an  exis3ng  method  of  an  object  

Facade   provides  a  simplified  interface  to  a  large  body  of  code  

BEHAVIORAL  

Chain  of  responsibility   delegates  commands  to  a  chain  of  processing  objects  

Command   creates  objects  which  encapsulate  ac3ons  and  parameters  

Mediator   allows  loose  coupling  between  classes  by  being  the  only  class  that  has  detailed  knowledge  of  their  methods  

Observer   is  a  publish/subscribe  paSern  which  allows  a  number  of  observer  objects  to  see  an  event  

Composite  

//"Single"elements!$("#singleItem").addClass("active");!$("#container").addClass("active");!!//"Collections"of"elements!$("div").addClass("active");!$(".item").addClass("active");!$("input").addClass("active");!!

Adapter  

//"Cross"browser"opacity:!//"opacity:"0.9;""Chrome"4+,"FF2+,"Saf3.1+,"Opera"9+,"IE9,"iOS"3.2+,"Android"2.1+!//"filter:"alpha(opacity=90);""IE6KIE8!!//"Setting"opacity!$(".container").css({!opacity:.5!});!!//"Getting"opacity!var!currentOpacity!=!$(".container").css('opacity');! !

Observer  

//"Equivalent"to"subscribe(topicName,"callback)!$(document).on("topicName",!function!()!{!!!!!//..perform"some"behaviour!});!!//"Equivalent"to"publish(topicName)!$(document).trigger("topicName");!!//"Equivalent"to"unsubscribe(topicName)!$(document).off("topicName");! !

AMD  -­‐  Asynchronous  module  defini3on  

define('''''module_id'/*optional*/,'''''[dependencies]'/*optional*/,'''''definition'function'/*function-for-instantiating-the-module-or-object*/')'!!!require(["foo",'"bar"],'function'(foo,'bar)'{'''''//-rest-of-your-code-here'''''foo.doSomething();'});'!

Prolifera3on  of  MV*  frameworks  

Prolifera3on  of  MV*  frameworks  

•  Rich  internet  single  page  applica3ons  •  Data  Binding  •  Client  side  templates  •  Client-­‐Centric  or  Server-­‐Centric-­‐  that  is  the  ques3on.  

•  Minify,  concatenate,  compress  •  Load  scripts  at  the  end  of  pages  •  Sta3c  code  analysis  using  Jslint/Jshint  •  Tes3ng  your  code  –  Jasmine,  Phantomjs  etc  

References  

Ques:ons?  

Recommended