JavaScript: Advanced Scoping & Other Puzzles
- 2. Doug Hendricks
Solutions Architect
Sencha Services
Monday, 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
Components
Monday, 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 : global
Monday, 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.say
Monday, 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 resolution
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 : undefined
Monday, 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 : undefined
Monday, 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 : function
Monday, 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 : function
Monday, 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 : function
Monday, 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 : function
Monday, 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 : function
Monday, 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 : function
Monday, 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 : function
Monday, 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 : function
Monday, 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
- 28. Extra fat ?
(Scope Chain Augmentation)
Monday, November 15, 2010
- 31. Scope Chain Augmentation
The big offenders:
Closures
with Clause
catch clause of try/catch
Monday, 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 : object
Monday, 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 : object
Monday, 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 : object
Monday, 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 : object
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
- 44. Let’s just eat Lard !
(catch in try/catch)
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 : object
Monday, 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 : object
Monday, 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
- 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 2900
Monday, 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 5300
Monday, 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 8800
Monday, 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
- 68. If you guessed:
b: at least 5300...
You’re Correct!
but, you can’t leave early!
Monday, 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
- 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?42942
Monday, 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
- 89. Thank You!
Hackathon : 4:30pm
Enjoy the remainder of
Senchacon 2010
Monday, November 15, 2010