Your SlideShare is downloading. ×
JavaScript: Advanced Scoping & Other Puzzles
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Saving this for later?

Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime - even offline.

Text the download link to your phone

Standard text messaging rates apply

JavaScript: Advanced Scoping & Other Puzzles

4,071
views

Published on

In this session, we'll review the fundamentals of Javascript variable scope and common "execution context" (scope) challenges associated with early/late binding of event handlers, specifically within …

In this session, we'll review the fundamentals of Javascript variable scope and common "execution context" (scope) challenges associated with early/late binding of event handlers, specifically within complex Ext JS layouts. We'll also bring several patterns (namespaced references, Function closures, inline references, ref/refOwner, and the "Poor-man's message bus") to bear on the bowl of soup we call "scope."

Published in: Technology

1 Comment
4 Likes
Statistics
Notes
No Downloads
Views
Total Views
4,071
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
146
Comments
1
Likes
4
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Monday, November 15, 2010
  • 2. Doug Hendricks Solutions Architect Sencha ServicesMonday, November 15, 2010
  • 3. ECMA Scope Soup ‘From the can’, alternate recipes, and puzzles.Monday, November 15, 2010
  • 4. On the menu: • ECMA Javascript variables and scope • In the can: Activation objects, scope chains, name resolution, and execution context • Too much salt (common mistakes) • Ext Observable binding contexts • Alternate binding approaches for Ext ComponentsMonday, November 15, 2010
  • 5. Global Variables Global variables - exist throughout the life of a script. Also considered public, they are: those NOT declared within a function block declared ANYWHERE in your script without the var keyword <script type="text/javascript"> globalB = 4; var globalA = 3, say = console.log; </script>Monday, November 15, 2010
  • 6. What is the global object(namespace)? Browsers, GreaseMonkey : window HTML5 Workers, Node.Js : globalMonday, November 15, 2010
  • 7. Global Variables <script type="text/javascript"> globalB = 4; var globalA = 3, say = console.log; </script> or, referenced through the global object as: window.globalA window.sayMonday, November 15, 2010
  • 8. Local Variables Local variables - survive only as long as the Function block they are declared in has an execution context. <script type="text/javascript"> var globalA, say = console.log; function doIt ( ) { var localA = 5; //local scope only } </script>Monday, November 15, 2010
  • 9. Something wrong here? <script type="text/javascript"> var globalA, say = console.log, a = 4; doIt(); function doIt ( ) { say ( a ); var a = 5; say( ++a ); } </script>Monday, November 15, 2010
  • 10. Something wrong here? <script type="text/javascript"> var globalA, say = console.log, a = 4; doIt(); function doIt ( ) { say ( a ); var a = 5; say( ++a ); } </script>Monday, November 15, 2010
  • 11. Something wrong here? <script type="text/javascript"> var globalA, say = console.log, a = 4; doIt(); <- this can’t work! function doIt ( ) { say ( a ); <- this is 4, right? var a = 5; say( ++a ); } </script>Monday, November 15, 2010
  • 12. <script type="text/javascript"> var globalA, say = console.log, a = 4; doIt(); <- sure it does, why? function doIt ( ) { say ( a ); <- undefined! why? var a = 5; say( ++a ); } </script>Monday, November 15, 2010
  • 13. introducing... activation objects, scope chain, identifier resolutionMonday, November 15, 2010
  • 14. Execution context initialized containing a ‘root’ or global object.Monday, November 15, 2010
  • 15. Execution context initialized containing a ‘root’ or global object. <script type="text/javascript"> Scope chain var globalA, say = console.log, global a = 4; window : object doIt(); document : object function doIt ( ) { navigator : object say ( a ); a:4 doIt : function var a = 5; globalA : undefined say( ++a ); say : function } </script>Monday, November 15, 2010
  • 16. next, an activation object is prepended to the scope-chain by first scanning the function body for local var’s: global window : object document : object navigator : object a:4 doIt : function globalA : undefinedMonday, November 15, 2010
  • 17. next, an activation object is prepended to the scope-chain by first scanning the function body for local var’s: Scope chain doIt(); activation function doIt ( ) { arguments : [] say ( a ); this : window a : undefined var a = 5; say( ++a ); global } window : object document : object navigator : object thus, a new context is created a:4 for doIt, containing a local ‘a’ doIt : function globalA : undefinedMonday, November 15, 2010
  • 18. Identifier resolution: get me ‘say’ ! (the hunt begins) Scope chain function doIt ( ) { activation say ( a ); arguments : [] var a = 5; this : window a : undefined say( ++a ); } global window : object document : object navigator : object a:4 say : functionMonday, November 15, 2010
  • 19. Identifier resolution: get me ‘say’ ! (the hunt begins) Scope chain function doIt ( ) { activation say ( a ); arguments : [] var a = 5; local?, nope! this : window a : undefined say( ++a ); } global window : object document : object navigator : object a:4 say : functionMonday, November 15, 2010
  • 20. Identifier resolution: get me ‘say’ ! (the hunt begins) Scope chain function doIt ( ) { activation say ( a ); arguments : [] var a = 5; local?, nope! this : window a : undefined say( ++a ); } global window : object document : object global has it! navigator : object a:4 say : functionMonday, November 15, 2010
  • 21. Now, on to the function argument: ‘a’ Scope chain function doIt ( ) { activation arguments : [] say ( a ); this : window var a = 5; a : undefined say( ++a ); } global window : object document : object navigator : object a:4 say : functionMonday, November 15, 2010
  • 22. Now, on to the function argument: ‘a’ Scope chain function doIt ( ) { activation arguments : [] say ( a ); <- prints: undefined this : window var a = 5; <- NOT 5! a : undefined say( ++a ); } global window : object document : object navigator : object a:4 say : functionMonday, November 15, 2010
  • 23. Now, on to assignment... Scope chain function doIt ( ) { activation say ( a ); arguments : [] var a = 5; this : window say( ++a ); a : undefined } global window : object document : object navigator : object a:4 say : functionMonday, November 15, 2010
  • 24. Now, on to assignment... Scope chain function doIt ( ) { activation say ( a ); arguments : [] var a = 5; local? yes, set it! this : window say( ++a ); a : undefined a:5 } global window : object document : object navigator : object a:4 and so on... say : functionMonday, November 15, 2010
  • 25. When function doIt is completed, it’s execution context (scope chain) is destroyed: doIt(); Scope chain function doIt ( ) { activation say ( a ); arguments : [] var a = 5; this : window say( ++a ); a:5 } global window : object document : object navigator : object a:4 say : functionMonday, November 15, 2010
  • 26. When function doIt is completed, it’s execution context (scope chain) is destroyed: doIt(); function doIt ( ) { say ( a ); var a = 5; say( ++a ); } and life continues, until the next function block...Monday, November 15, 2010
  • 27. Monday, November 15, 2010
  • 28. Extra fat ? (Scope Chain Augmentation)Monday, November 15, 2010
  • 29. (Scope Chain Augmentation)Monday, November 15, 2010
  • 30. Scope Chain Augmentation The big offenders:Monday, November 15, 2010
  • 31. Scope Chain Augmentation The big offenders: Closures with Clause catch clause of try/catchMonday, November 15, 2010
  • 32. Scope Chain Augmentation Function Closures var trimString = function () { var reReplace = /^s+|s+$/g; return ( function (str){ return str.replace(reReplace, ‘’); } ); }( ); <- create the closure global window : object document : object navigator : objectMonday, November 15, 2010
  • 33. Scope Chain Augmentation Function Closures var trimString = function () { activation var reReplace = /^s+|s+$/g; arguments : [str] this : window return ( function (str){ return str.replace(reReplace, ‘’); activation arguments : [] } ); this : window }( ); <- create the closure reReplace : RegExp global window : object document : object navigator : objectMonday, November 15, 2010
  • 34. Scope Chain Augmentation Function Closures var trimString = function () { activation var reReplace = /^s+|s+$/g; arguments : [str] this : window return ( function (str){ return str.replace(reReplace, ‘’); activation arguments : [] } ); this : window }( ); <- create the closure reReplace : RegExp Closures will always have 3 global window : object scope chain members, document : object navigator : object minimum!Monday, November 15, 2010
  • 35. Scope Chain Augmentation Function Closures function puffEmUp () { var els = Ext.select(div), doc = Ext.getDoc(); els.addClass(puff); doc.on({ click : function(e, target){ els.removeClass(‘puff’); els.highlight(); }, ‘delegate’ : div.puff, ‘single’ : true global }); window : object } document : object navigator : objectMonday, November 15, 2010
  • 36. Scope Chain Augmentation Function Closures function puffEmUp () { activation var els = Ext.select(div), arguments : [e, target] doc = Ext.getDoc(); this : Ext.Element els.addClass(puff); activation doc.on({ arguments : [] click : function(e, target){ this : window els.removeClass(‘puff’); els : Ext.CompositeEl... els.highlight(); doc : Ext.Element }, ‘delegate’ : div.puff, ‘single’ : true global }); window : object } document : object navigator : objectMonday, November 15, 2010
  • 37. Monday, November 15, 2010
  • 38. Even more fat !? (with Clause)Monday, November 15, 2010
  • 39. Monday, November 15, 2010
  • 40. Scope Chain Augmentation ‘with’ Clause function puffEmUp () { var els = Ext.select(div), doc = Ext.getDoc(); els.addClass(puff); doc.on({ click : function(e, target){ with (els) { removeClass(‘puff’); highlight(); } }, ‘delegate’ : div.puff, ‘single’ : true }); }Monday, November 15, 2010
  • 41. Scope Chain Augmentation ‘with’ Clause function puffEmUp () { var els = Ext.select(div), doc = Ext.getDoc(); els.addClass(puff); doc.on({ activation click : function(e, target){ arguments : [e, target] with (els) { removeClass(‘puff’); activation highlight(); arguments : [] } }, global ‘delegate’ : div.puff, ‘single’ : true window : object }); }Monday, November 15, 2010
  • 42. Scope Chain Augmentation ‘with’ Clause function puffEmUp () { var els = Ext.select(div), variable doc = Ext.getDoc(); els : Ext.CompositeEl... els.addClass(puff); doc.on({ activation click : function(e, target){ arguments : [e, target] with (els) { removeClass(‘puff’); activation highlight(); arguments : [] } }, global ‘delegate’ : div.puff, ‘single’ : true window : object }); }Monday, November 15, 2010
  • 43. Monday, November 15, 2010
  • 44. Let’s just eat Lard ! (catch in try/catch)Monday, November 15, 2010
  • 45. Monday, November 15, 2010
  • 46. Scope Chain Augmentation catch block doc.on({ click : function(e, target){ try{ with (els) { removeClass(‘puff’); highlight(); } } catch( err ) { Ext.MessageBox.alert(‘Ooops’); } },Monday, November 15, 2010
  • 47. Scope Chain Augmentation catch block doc.on({ click : function(e, target){ try{ activation with (els) { arguments : [e, target] removeClass(‘puff’); highlight(); } activation } catch( err ) { arguments : [] Ext.MessageBox.alert(‘Ooops’); } global }, window : objectMonday, November 15, 2010
  • 48. Scope Chain Augmentation catch block variable arguments : [err] doc.on({ click : function(e, target){ try{ activation with (els) { arguments : [e, target] removeClass(‘puff’); highlight(); } activation } catch( err ) { arguments : [] Ext.MessageBox.alert(‘Ooops’); } global }, window : objectMonday, November 15, 2010
  • 49. Optimizations function puffEmUp () { var els = Ext.select(div), doc = Ext.getDoc(); els.addClass(puff); doc.on({ click : function(e, target){ els.removeClass(‘puff’); els.highlight(); }, ‘delegate’ : div.puff, ‘single’ : true }); }Monday, November 15, 2010
  • 50. Optimizations Expensive function puffEmUp () { var els = Ext.select(div), doc = Ext.getDoc(); els.addClass(puff); doc.on({ click : function(e, target){ els.removeClass(‘puff’); els.highlight(); }, ‘delegate’ : div.puff, ‘single’ : true }); }Monday, November 15, 2010
  • 51. Optimizations Expensive Better function puffEmUp () { function puffEmUp () { var els = Ext.select(div), var E = Ext, doc = Ext.getDoc(); els = E.select(div); els.addClass(puff); els.addClass(puff); doc.on({ E.getDoc().on({ click : function(e, target){ click : function(e, target){ els.removeClass(‘puff’); els.highlight(); var collect = els; }, collect.removeClass(‘puff’); ‘delegate’ : div.puff, collect.highlight(); ‘single’ : true }, }); ‘delegate’ : div.puff, } ‘single’ : true }); }Monday, November 15, 2010
  • 52. Optimize Further function puffEmUp () { var E = Ext, els = E.select(div); function puffEmUp () { var E = Ext, els.addClass(puff); E.getDoc().on({ els = E.select(div); click : function(e, target){ try{ els.addClass(puff); this.removeClass(‘puff’); E.getDoc().on({ this.highlight(); click : function(e, target){ } catch (err) { / a compromise / var collect = els; E.MessageBox.alert(‘Oops’); collect.removeClass(‘puff’); } collect.highlight(); }, }, ‘delegate’ : div.puff, ‘scope’ : els, ‘single’ : true ‘delegate’ : div.puff, }); ‘single’ : true } }); }Monday, November 15, 2010
  • 53. Optimize Further function puffEmUp () { Better var E = Ext, els = E.select(div); function puffEmUp () { var E = Ext, els.addClass(puff); E.getDoc().on({ els = E.select(div); click : function(e, target){ try{ els.addClass(puff); this.removeClass(‘puff’); E.getDoc().on({ this.highlight(); click : function(e, target){ } catch (err) { / a compromise / var collect = els; E.MessageBox.alert(‘Oops’); collect.removeClass(‘puff’); } collect.highlight(); }, }, ‘delegate’ : div.puff, ‘scope’ : els, ‘single’ : true ‘delegate’ : div.puff, }); ‘single’ : true } }); }Monday, November 15, 2010
  • 54. Leverage Execution Context function puffEmUp () { var E = Ext, els = E.select(div); els.addClass(puff); E.getDoc().on({ click : function(e, target){ try{ this.removeClass(‘puff’); this.highlight(); } catch (err) { / a compromise / E.MessageBox.alert(‘Oops’); } }, ‘scope’ : els, ‘delegate’ : div.puff, ‘single’ : true }); }Monday, November 15, 2010
  • 55. Leverage Execution Context function puffEmUp () { var E = Ext, els = E.select(div); els.addClass(puff); Replace scope- E.getDoc().on({ click : function(e, target){ chain traversal try{ this.removeClass(‘puff’); with a single this.highlight(); context (this) } catch (err) { / a compromise / prototype } E.MessageBox.alert(‘Oops’); search. }, ‘scope’ : els, ‘delegate’ : div.puff, ‘single’ : true }); }Monday, November 15, 2010
  • 56. Recommendations Declare frequently used function variables as locals. Promote frequently used globals UP the scope chain (creating local references to them as necessary) Use closures and try/catch handlers sparingly. Forget about the ‘with’ clause! (deprecated in ECMA Javascript 5)Monday, November 15, 2010
  • 57. Why is this important at all?Monday, November 15, 2010
  • 58. trivia time!Monday, November 15, 2010
  • 59. trivia time! Can you guess how many function definitions there are in the Ext 3.3 framework?Monday, November 15, 2010
  • 60. Can you guess how many function definitions there are in the Ext 3.3 framework?Monday, November 15, 2010
  • 61. Can you guess how many function definitions there are in the Ext 3.3 framework? Is it:Monday, November 15, 2010
  • 62. Can you guess how many function definitions there are in the Ext 3.3 framework? Is it: a: at least 2900Monday, November 15, 2010
  • 63. Can you guess how many function definitions there are in the Ext 3.3 framework? Is it: a: at least 2900 b: at least 5300Monday, November 15, 2010
  • 64. Can you guess how many function definitions there are in the Ext 3.3 framework? Is it: a: at least 2900 b: at least 5300 c: at least 8800Monday, November 15, 2010
  • 65. Can you guess how many function definitions there are in the Ext 3.3 framework? Is it: a: at least 2900 b: at least 5300 c: at least 8800 d: omg! I can’t count that high !Monday, November 15, 2010
  • 66. If you guessed: b: at least 5300...Monday, November 15, 2010
  • 67. If you guessed: b: at least 5300...Monday, November 15, 2010
  • 68. If you guessed: b: at least 5300... You’re Correct! but, you can’t leave early!Monday, November 15, 2010
  • 69. Common Mistakes and BottlenecksMonday, November 15, 2010
  • 70. Where did it go? <script type="text/javascript"> function doIt ( ) { var a = 5; say( ++a ); } setTimeout( ‘doIt();‘ , 1000); </script>Monday, November 15, 2010
  • 71. Where did it go? <script type="text/javascript"> <script type="text/javascript"> var doIt = function ( ) { function doIt ( ) { var a = 5; var a = 5; say( ++a ); say( ++a ); } setTimeout( ‘doIt();‘ , 1000); } </script> setTimeout( ‘doIt();‘ , 1000); </script>Monday, November 15, 2010
  • 72. Where did it go? <script type="text/javascript"> <script type="text/javascript"> var doIt = function ( ) { function doIt ( ) { var a = 5; var a = 5; say( ++a ); say( ++a ); } setTimeout( ‘doIt();‘ , 1000); } </script> setTimeout( ‘doIt();‘ , 1000); </script> create a global reference!Monday, November 15, 2010
  • 73. Identifier Resolution Mayhem! var getAddress = function(){ return ( some.favorite.customer.we.love.name + ‘n’ + some.favorite.customer.we.love.address1 + ‘n’ + some.favorite.customer.we.love.address2 + ‘n’ + some.favorite.customer.we.love.city + ‘n’ + some.favorite.customer.we.love.state + ‘n’ + some.favorite.customer.we.love.zip ); };Monday, November 15, 2010
  • 74. Identifier Resolution Mayhem! var getAddress = function(){ return ( some.favorite.customer.we.love.name + ‘n’ + some.favorite.customer.we.love.address1 + ‘n’ + some.favorite.customer.we.love.address2 + ‘n’ + some.favorite.customer.we.love.city + ‘n’ + some.favorite.customer.we.love.state + ‘n’ + some.favorite.customer.we.love.zip ); }; Don’t be a copy/paste victim !Monday, November 15, 2010
  • 75. Identifier Resolution Optimized! var getAddress = function () { //resolve the global once! var cust = some.favorite.customer.we.love; return [ cust.name, cust.address1, cust.address2, cust.city, cust.state, cust.zip ].join(‘n’); };Monday, November 15, 2010
  • 76. Iteration (with calories) Function-based Ext.each (iterable, function) Ext.iterate (iterable, function) $each $jQuery.each( iterable, function ) Enumerable.each( iterable, function ) Array.forEach(function)Monday, November 15, 2010
  • 77. Iteration (with calories) Function-based Ext.each (iterable, function) Ext.iterate (iterable, function) $each $jQuery.each( iterable, function ) Enumerable.each( iterable, function ) Array.forEach(function) These iterators create additional scope chains. Reserve for light-duty use only!Monday, November 15, 2010
  • 78. Traditional event binding strategiesMonday, November 15, 2010
  • 79. Classic Quandary { xtype: ‘grid’, store : ‘storeId’, buttons : [{ text : ‘Remove Item’, handler : function(){..} , scope : ??? },{ text : ‘Close’, handler : function(){..}, scope: ??? }] }Monday, November 15, 2010
  • 80. { xtype: ‘grid’, store : ‘storeId’, initComponent : function(){ / /template method this.buttons = [{ text : ‘Remove Item’, iconCls : ‘remove-icon’, handler : this.removeItem , scope : this },{ text : ‘Close’, handler : this.destroy, scope : this }]; this.constructor.prototype.initComponent.call(this); }, removeItem : function(button){ var record = this.getSelectionModel().getSelected(); ....... //remove the entity... } }Monday, November 15, 2010
  • 81. Bring your desired scope into context { (without sub-classing) xtype: ‘grid’, store : ‘storeId’, initComponent : function(){ / /template method this.buttons = [{ text : ‘Remove Item’, iconCls : ‘remove-icon’, handler : this.removeItem , scope : this },{ text : ‘Close’, handler : this.destroy, scope : this }]; this.constructor.prototype.initComponent.call(this); }, removeItem : function(button){ var record = this.getSelectionModel().getSelected(); ....... //remove the entity... } }Monday, November 15, 2010
  • 82. Poor-man’s Message Bus Revised version of Ext.util.Observable class Mechanism to loosely-couple behaviors using events (messaging) Event binding complexity reduced for most situations.Monday, November 15, 2010
  • 83. (function(){       Ext.extend(           Ext.ux.MsgBus = function(config){                this.events = {};                Ext.apply(this,config||{});                Ext.ux.MsgBus.superclass.constructor.call(this);             },                Ext.util.Observable,          {             publish : function(topic /* ,variable arguments ,,, */  ){                  var t = String(topic);                  this.events[t] || (this.addEvents(t));                  return this.fireEvent.apply( this,  [t].concat(Array.prototype.slice.call(arguments,1)) );             }         } );        var uxp = Ext.ux.MsgBus.prototype;        Ext.apply(uxp,{           subscribe    : uxp.on,      //aliases           unsubscribe  : uxp.un,           destroy      : uxp.purgeListeners        }); })(); Follow along at: http://www.sencha.com/forum/showthread.php?42942Monday, November 15, 2010
  • 84. Why not as a singleton? Poor candidates for Unit testing as ‘state’ is unpredictable over time, cannot be reset. Inhibits implementation flexibility. The class can be extended/overloaded further to handle custom messaging behaviors.Monday, November 15, 2010
  • 85. Basic ux.MsgBus sample Ext.ns(your); your.bus = new Ext.ux.MsgBus(); your.bus.subscribe(test,        function(){ console.log(arguments); },       context,       {single:true, delay: 500 }     //standard Observable arguments and options     ); var wasCancelled = ( your.bus.publish( test,  this is only a test,  someObj,  someArray) === false); Monday, November 15, 2010
  • 86. Multi-Channel Ext.namespace(your); your.bus = { channels: { // slots, topics (call it what you will) chat : new Ext.ux.MsgBus(), feeds : new Ext.ux.MsgBus(), orders : new Ext.ux.MsgBus() }, destroy : function(){ Ext.iterate(this.channels, Ext.destroy); } }; var channels = your.bus.channels; your.control = { removeItem : function(itemRecord){ var success; //handle order removal centrally here (via data.Store, Ajax, etc) success ? channels.orders.publish(‘itemremoved’, itemRecord) : channels.orders.publish(‘itemremovalfailure’, itemRecord, response); } }; channels.orders.subscribe({ remove : your.control.removeItem, ‘cancel’ : your.control.cancelOrder });      Monday, November 15, 2010
  • 87. { xtype: ‘grid’, store : ‘storeId’, initComponent : function(){ / /template method this.buttons = [{ text : ‘Remove Item’, iconCls : ‘remove-icon’, handler : this.removeItem , scope : this },{ text : ‘Close’, handler : this.destroy, scope : this }]; this.constructor.prototype.initComponent.call(this); }, removeItem : function(button){ var record = this.getSelectionModel().getSelected(), CB = function( success) { success && button.enable(); }; channels.orders.publish(‘remove’, record, CB ); } }Monday, November 15, 2010
  • 88. Questions ?Monday, November 15, 2010
  • 89. Thank You! Hackathon : 4:30pm Enjoy the remainder of Senchacon 2010Monday, November 15, 2010