Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Advanced Prototype.js

Advanced Prototype.js

Bacb1158b77a142d2cb8ae1f866c75f1?s=128

Christophe Porteneuve

July 25, 2007
Tweet

More Decks by Christophe Porteneuve

Other Decks in Technology

Transcript

  1. Advanced Prototype Christophe Porteneuve The Ajax Experience · San Francisco,

    July 25 2007
  2. Did you attend Intro?

  3. Did you attend Intro? • This was earlier this morning.

    We saw...
  4. Did you attend Intro? • This was earlier this morning.

    We saw... ‣ A few noteworthy JavaScript aspects
  5. Did you attend Intro? • This was earlier this morning.

    We saw... ‣ A few noteworthy JavaScript aspects ‣ Extensions to native types (arrays, strings...)
  6. Did you attend Intro? • This was earlier this morning.

    We saw... ‣ A few noteworthy JavaScript aspects ‣ Extensions to native types (arrays, strings...) ‣ Extensions to DOM elements
  7. Did you attend Intro? • This was earlier this morning.

    We saw... ‣ A few noteworthy JavaScript aspects ‣ Extensions to native types (arrays, strings...) ‣ Extensions to DOM elements ‣ Event unified handling
  8. Did you attend Intro? • This was earlier this morning.

    We saw... ‣ A few noteworthy JavaScript aspects ‣ Extensions to native types (arrays, strings...) ‣ Extensions to DOM elements ‣ Event unified handling ‣ AJAX support
  9. “So you’re qualified cuz...?”

  10. “So you’re qualified cuz...?” • Prototype Core member

  11. “So you’re qualified cuz...?” • Prototype Core member • Rails

    & script.aculo.us contributor
  12. “So you’re qualified cuz...?” • Prototype Core member • Rails

    & script.aculo.us contributor • Prototype doc writer (prototypejs.org)
  13. “So you’re qualified cuz...?” • Prototype Core member • Rails

    & script.aculo.us contributor • Prototype doc writer (prototypejs.org) • Prototype book author
  14. “So you’re qualified cuz...?” • Prototype Core member • Rails

    & script.aculo.us contributor • Prototype doc writer (prototypejs.org) • Prototype book author • Top question batter on the Google Group
  15. “Advanced,” like, how?

  16. “Advanced,” like, how? • Function binding, wrapping & methodizing

  17. “Advanced,” like, how? • Function binding, wrapping & methodizing •

    Enumerable best practices
  18. “Advanced,” like, how? • Function binding, wrapping & methodizing •

    Enumerable best practices • Event best practices
  19. “Advanced,” like, how? • Function binding, wrapping & methodizing •

    Enumerable best practices • Event best practices • JSON love
  20. “Advanced,” like, how? • Function binding, wrapping & methodizing •

    Enumerable best practices • Event best practices • JSON love • Custom DOM extensions, custom classes
  21. “Advanced,” like, how? • Function binding, wrapping & methodizing •

    Enumerable best practices • Event best practices • JSON love • Custom DOM extensions, custom classes • Sneak peek at upcoming 1.6!
  22. Should I know Prototype a bit already?

  23. Should I know Prototype a bit already? • Well... yeah.

    It would help.
  24. Should I know Prototype a bit already? • Well... yeah.

    It would help. • We won’t go over each syntax and basic class here.
  25. Should I know Prototype a bit already? • Well... yeah.

    It would help. • We won’t go over each syntax and basic class here. • If you’re proficient at JavaScript, that may compensate.
  26. Function-Fu

  27. Method scopes (and other horror stories)

  28. Method scopes (and other horror stories) var name = 'a

    JavaScript n00b'; var obj = { name: 'Scruffy', greet: function() { return "Hi, I’m " + this.name; } }; function sayHi(greetFx) { alert(greetFx()); }
  29. Method scopes (and other horror stories) var name = 'a

    JavaScript n00b'; var obj = { name: 'Scruffy', greet: function() { return "Hi, I’m " + this.name; } }; function sayHi(greetFx) { alert(greetFx()); } sayHi(obj.greet);
  30. Method scopes (and other horror stories) var name = 'a

    JavaScript n00b'; var obj = { name: 'Scruffy', greet: function() { return "Hi, I’m " + this.name; } }; function sayHi(greetFx) { alert(greetFx()); } sayHi(obj.greet); // Alerts "Hi, I’m a JavaScript n00b"
  31. What the hell just happened?

  32. What the hell just happened? • We passed a reference

    to a method sayHi(obj.greet);
  33. What the hell just happened? • We passed a reference

    to a method ‣ It lost its binding: the scope to which “this” refers.
  34. What the hell just happened? • We passed a reference

    to a method ‣ It lost its binding: the scope to which “this” refers. • How come it usually works then?
  35. What the hell just happened? • We passed a reference

    to a method ‣ It lost its binding: the scope to which “this” refers. • How come it usually works then? ‣ You call methods directly on objects!
  36. What the hell just happened? • We passed a reference

    to a method ‣ It lost its binding: the scope to which “this” refers. • How come it usually works then? ‣ You call methods directly on objects! • OK, so how can I fix it?
  37. What the hell just happened? • We passed a reference

    to a method ‣ It lost its binding: the scope to which “this” refers. • How come it usually works then? ‣ You call methods directly on objects! • OK, so how can I fix it? ‣ By using bind, of course!
  38. One call to bind them all

  39. One call to bind them all • bind “attaches” a

    scope object to a method
  40. One call to bind them all • bind “attaches” a

    scope object to a method ‣ Actually returns an ad hoc anonymous function
  41. One call to bind them all • bind “attaches” a

    scope object to a method ‣ Actually returns an ad hoc anonymous function ‣ So you may want to cache it if you plan on re-using it (say, register/deregister in a repository)
  42. One call to bind them all • bind “attaches” a

    scope object to a method ‣ Actually returns an ad hoc anonymous function ‣ So you may want to cache it if you plan on re-using it (say, register/deregister in a repository) • Relies on JavaScript’s native apply method.
  43. One call to bind them all • bind “attaches” a

    scope object to a method ‣ Actually returns an ad hoc anonymous function ‣ So you may want to cache it if you plan on re-using it (say, register/deregister in a repository) • Relies on JavaScript’s native apply method. sayHi(obj.greet.bind(obj))
  44. One call to bind them all • bind “attaches” a

    scope object to a method ‣ Actually returns an ad hoc anonymous function ‣ So you may want to cache it if you plan on re-using it (say, register/deregister in a repository) • Relies on JavaScript’s native apply method. sayHi(obj.greet.bind(obj)) // Alerts "Hi, I’m Scruffy!"
  45. One call to bind them all • bind “attaches” a

    scope object to a method ‣ Actually returns an ad hoc anonymous function ‣ So you may want to cache it if you plan on re-using it (say, register/deregister in a repository) • Relies on JavaScript’s native apply method. sayHi(obj.greet.bind(obj)) // Alerts "Hi, I’m Scruffy!" var fx = obj.greet.bind(obj); sayHi(fx);
  46. One call to bind them all • bind “attaches” a

    scope object to a method ‣ Actually returns an ad hoc anonymous function ‣ So you may want to cache it if you plan on re-using it (say, register/deregister in a repository) • Relies on JavaScript’s native apply method. sayHi(obj.greet.bind(obj)) // Alerts "Hi, I’m Scruffy!" var fx = obj.greet.bind(obj); sayHi(fx); // Same...
  47. But wait! There’s more! • Most people don’t realize this,

    but bind lets you pre- fill arguments, too!
  48. But wait! There’s more! • Most people don’t realize this,

    but bind lets you pre- fill arguments, too! var obj = { base: [1, 2, 3], listAll: function() { return this.base.concat($A(arguments)).join(','); } }
  49. But wait! There’s more! • Most people don’t realize this,

    but bind lets you pre- fill arguments, too! var obj = { base: [1, 2, 3], listAll: function() { return this.base.concat($A(arguments)).join(','); } } var call = obj.listAll.bind(obj, 4, 5, 6);
  50. But wait! There’s more! • Most people don’t realize this,

    but bind lets you pre- fill arguments, too! var obj = { base: [1, 2, 3], listAll: function() { return this.base.concat($A(arguments)).join(','); } } var call = obj.listAll.bind(obj, 4, 5, 6); call();
  51. But wait! There’s more! • Most people don’t realize this,

    but bind lets you pre- fill arguments, too! var obj = { base: [1, 2, 3], listAll: function() { return this.base.concat($A(arguments)).join(','); } } var call = obj.listAll.bind(obj, 4, 5, 6); call(); // '1,2,3,4,5,6'
  52. Binding event listeners

  53. Binding event listeners • Event handlers expect the event object

    as their first argument
  54. Binding event listeners • Event handlers expect the event object

    as their first argument ‣ If you use bind with pre-filled arguments, you’ll push this event object aside...
  55. Binding event listeners • Event handlers expect the event object

    as their first argument ‣ If you use bind with pre-filled arguments, you’ll push this event object aside... • The one use case for bindAsEventListener
  56. Binding event listeners • Event handlers expect the event object

    as their first argument ‣ If you use bind with pre-filled arguments, you’ll push this event object aside... • The one use case for bindAsEventListener var obj = { // fields, other methods... function clickHandler(e, mode) { ... } }; var ch = obj.clickHandler.bindAsEventListener(obj, 'auto'); $(element).observe('click', ch);
  57. Binding or closure?

  58. Binding or closure? • Think twice before binding inner functions

  59. Binding or closure? • Think twice before binding inner functions

    ‣ Wrapping anonymous function: calls × 2
  60. Binding or closure? • Think twice before binding inner functions

    ‣ Wrapping anonymous function: calls × 2 ‣ In a fat loop: feel the pain?
  61. Binding or closure? • Think twice before binding inner functions

    ‣ Wrapping anonymous function: calls × 2 ‣ In a fat loop: feel the pain? ‣ Just save “this” and leverage the closure:
  62. Binding or closure? • Think twice before binding inner functions

    ‣ Wrapping anonymous function: calls × 2 ‣ In a fat loop: feel the pain? ‣ Just save “this” and leverage the closure: someMethod: function() { var obj = this; this.children.each(function(child) { obj.markChildProcessed(child); }); } No binding needed!
  63. Composing with wrap

  64. Composing with wrap • Composing a function into another: f(g(x))

  65. Composing with wrap • Composing a function into another: f(g(x))

    • Your outer function must expect its inner function as its first argument.
  66. Composing with wrap • Composing a function into another: f(g(x))

    • Your outer function must expect its inner function as its first argument. • Prominent use-case: AOP!
  67. Composing with wrap • Composing a function into another: f(g(x))

    • Your outer function must expect its inner function as its first argument. • Prominent use-case: AOP! function log() { var args = $A(arguments), proceed = args.shift(); console.log('>>> (' + args.invoke('inspect').join(', ') + ')'); proceed.call(this, args); console.log('<<< '); } lfx = fx.wrap(log); lfx('hello', 'world');
  68. Methodizing

  69. Methodizing • Yuck, sounds like a Bubble 1.0 slide!

  70. Methodizing • Yuck, sounds like a Bubble 1.0 slide! •

    Encodes the following pattern:
  71. Methodizing • Yuck, sounds like a Bubble 1.0 slide! •

    Encodes the following pattern: ‣ A function takes its subject as first argument
  72. Methodizing • Yuck, sounds like a Bubble 1.0 slide! •

    Encodes the following pattern: ‣ A function takes its subject as first argument ‣ We wish to equip subjects with it, as a direct method
  73. Methodizing • Yuck, sounds like a Bubble 1.0 slide! •

    Encodes the following pattern: ‣ A function takes its subject as first argument ‣ We wish to equip subjects with it, as a direct method • Prime example: Element.Methods (DOM
  74. Methodizing • Yuck, sounds like a Bubble 1.0 slide! •

    Encodes the following pattern: ‣ A function takes its subject as first argument ‣ We wish to equip subjects with it, as a direct method • Prime example: Element.Methods (DOM MyLib = { b0rk: function(element, mode, count) { ... } } MyLib.b0rk(elt, 'auto', 3); elt.b0rk = MyLib.b0rk.methodize(); elt.b0rk('auto', 3);
  75. Enumerable-Fu

  76. To each his own

  77. To each his own • Are you one of the

    many who over-use each?
  78. To each his own • Are you one of the

    many who over-use each? ‣ each is the generic, all-purpose iterator
  79. To each his own • Are you one of the

    many who over-use each? ‣ each is the generic, all-purpose iterator ‣ It has a significant cost: function call + closure
  80. To each his own • Are you one of the

    many who over-use each? ‣ each is the generic, all-purpose iterator ‣ It has a significant cost: function call + closure • Looking to produce derived values? Use map!
  81. To each his own • Are you one of the

    many who over-use each? ‣ each is the generic, all-purpose iterator ‣ It has a significant cost: function call + closure • Looking to produce derived values? Use map! ‣ Getting the same property on all? Use pluck!
  82. To each his own • Are you one of the

    many who over-use each? ‣ each is the generic, all-purpose iterator ‣ It has a significant cost: function call + closure • Looking to produce derived values? Use map! ‣ Getting the same property on all? Use pluck! ‣ Calling the same method on all? Use invoke!
  83. To each his own • Are you one of the

    many who over-use each? ‣ each is the generic, all-purpose iterator ‣ It has a significant cost: function call + closure • Looking to produce derived values? Use map! ‣ Getting the same property on all? Use pluck! ‣ Calling the same method on all? Use invoke! • Many, many more common use-cases are optimized
  84. map / collect

  85. map / collect • The generic transformation function

  86. map / collect • The generic transformation function ‣ Each

    element is turned into a derived value thanks to a custom transform you provide
  87. map / collect • The generic transformation function ‣ Each

    element is turned into a derived value thanks to a custom transform you provide • Returns an array of the results
  88. map / collect • The generic transformation function ‣ Each

    element is turned into a derived value thanks to a custom transform you provide • Returns an array of the results $R(1, 5).map(function(count) { return '*'.times(count); })
  89. map / collect • The generic transformation function ‣ Each

    element is turned into a derived value thanks to a custom transform you provide • Returns an array of the results $R(1, 5).map(function(count) { return '*'.times(count); }) // ['*', '**', '***', '****', '*****']
  90. map / collect • The generic transformation function ‣ Each

    element is turned into a derived value thanks to a custom transform you provide • Returns an array of the results $R(1, 5).map(function(count) { return '*'.times(count); }) // ['*', '**', '***', '****', '*****'] $w('Sometimes you gotta code your map').map(function(word) { return word.toArray().reverse().join(''); }).join(' ');
  91. map / collect • The generic transformation function ‣ Each

    element is turned into a derived value thanks to a custom transform you provide • Returns an array of the results $R(1, 5).map(function(count) { return '*'.times(count); }) // ['*', '**', '***', '****', '*****'] $w('Sometimes you gotta code your map').map(function(word) { return word.toArray().reverse().join(''); }).join(' '); // 'semitemoS uoy attog edoc ruoy pam'
  92. faster map: pluck

  93. faster map: pluck • Common map use-case

  94. faster map: pluck • Common map use-case • Simple transform:

    fetching the same property for each element
  95. faster map: pluck • Common map use-case • Simple transform:

    fetching the same property for each element • Much faster than a map
  96. faster map: pluck • Common map use-case • Simple transform:

    fetching the same property for each element • Much faster than a map $w('The Ajax Experience').pluck('length')
  97. faster map: pluck • Common map use-case • Simple transform:

    fetching the same property for each element • Much faster than a map $w('The Ajax Experience').pluck('length') // [3, 4, 10]
  98. faster map: pluck • Common map use-case • Simple transform:

    fetching the same property for each element • Much faster than a map $w('The Ajax Experience').pluck('length') // [3, 4, 10] $w('The Ajax Experience').pluck(0).join('')
  99. faster map: pluck • Common map use-case • Simple transform:

    fetching the same property for each element • Much faster than a map $w('The Ajax Experience').pluck('length') // [3, 4, 10] $w('The Ajax Experience').pluck(0).join('') // 'TAE' <-- 'The'[0] == 'T' ;-)
  100. faster map: invoke

  101. faster map: invoke • Common map use-case

  102. faster map: invoke • Common map use-case • Simple transform:

    calling the same method, with the same arguments
  103. faster map: invoke • Common map use-case • Simple transform:

    calling the same method, with the same arguments • Much faster than a map
  104. faster map: invoke • Common map use-case • Simple transform:

    calling the same method, with the same arguments • Much faster than a map • Still, chaining multiple calls can be costlier
  105. faster map: invoke • Common map use-case • Simple transform:

    calling the same method, with the same arguments • Much faster than a map • Still, chaining multiple calls can be costlier
  106. faster map: invoke • Common map use-case • Simple transform:

    calling the same method, with the same arguments • Much faster than a map • Still, chaining multiple calls can be costlier $w('Thou Shalt Not Be Longer Than 4').invoke('truncate', 4, '…').join('')
  107. faster map: invoke • Common map use-case • Simple transform:

    calling the same method, with the same arguments • Much faster than a map • Still, chaining multiple calls can be costlier $w('Thou Shalt Not Be Longer Than 4').invoke('truncate', 4, '…').join('') // 'Thou Sha… Not Be Lon… Than 4'
  108. faster map: invoke • Common map use-case • Simple transform:

    calling the same method, with the same arguments • Much faster than a map • Still, chaining multiple calls can be costlier $w('Thou Shalt Not Be Longer Than 4').invoke('truncate', 4, '…').join('') // 'Thou Sha… Not Be Lon… Than 4' $w('No vowel whatsoever').invoke('gsub', /[aeiouy]+/i, '').pluck('length')
  109. faster map: invoke • Common map use-case • Simple transform:

    calling the same method, with the same arguments • Much faster than a map • Still, chaining multiple calls can be costlier $w('Thou Shalt Not Be Longer Than 4').invoke('truncate', 4, '…').join('') // 'Thou Sha… Not Be Lon… Than 4' $w('No vowel whatsoever').invoke('gsub', /[aeiouy]+/i, '').pluck('length') // [1, 3, 6] <-- 'N vwl whtsvr'
  110. A rundown of the rest...

  111. A rundown of the rest... • eachSlice, inGroupsOf: cutting in

    slots
  112. A rundown of the rest... • eachSlice, inGroupsOf: cutting in

    slots • select, reject, partition, grep, all, any: global lookups, checks & filters
  113. A rundown of the rest... • eachSlice, inGroupsOf: cutting in

    slots • select, reject, partition, grep, all, any: global lookups, checks & filters • find/detect, include: single lookup
  114. A rundown of the rest... • eachSlice, inGroupsOf: cutting in

    slots • select, reject, partition, grep, all, any: global lookups, checks & filters • find/detect, include: single lookup • inject: computing one value out of it all
  115. A rundown of the rest... • eachSlice, inGroupsOf: cutting in

    slots • select, reject, partition, grep, all, any: global lookups, checks & filters • find/detect, include: single lookup • inject: computing one value out of it all • zip, toArray, size, inspect, min, max...
  116. Event-Fu

  117. Save handlers: use bubbling!

  118. Save handlers: use bubbling! • The Ugly: multiple bind[AsEventListener] on

    the same handler elements.each(function(e) { e.observe('click', this.handleClick.bind(this)); });
  119. Save handlers: use bubbling! • The Ugly: multiple bind[AsEventListener] on

    the same handler • The Bad: multiple registrations of the same handler elements.invoke('observe', 'click', this.handleClick.bind(this));
  120. Save handlers: use bubbling! • The Ugly: multiple bind[AsEventListener] on

    the same handler • The Bad: multiple registrations of the same handler • The Good: single registration on container, using the bubbling and Event.element var handler = this.handleClick.bind(this); container.observe('click', handler);
  121. element or findElement?

  122. element or findElement? • Event.element returns the element the event

    fired on (IE: source. W3: target)
  123. element or findElement? • Event.element returns the element the event

    fired on (IE: source. W3: target) ‣ Sometimes you’re not interested in it: perhaps bubbling means your registered ‘click’ on an a, and this is a span inside it...
  124. element or findElement? • Event.element returns the element the event

    fired on (IE: source. W3: target) ‣ Sometimes you’re not interested in it: perhaps bubbling means your registered ‘click’ on an a, and this is a span inside it... • Event.findElement lets you specify a CSS3 selector used to walk up the element’s ancestry.
  125. element or findElement? • Event.element returns the element the event

    fired on (IE: source. W3: target) ‣ Sometimes you’re not interested in it: perhaps bubbling means your registered ‘click’ on an a, and this is a span inside it... • Event.findElement lets you specify a CSS3 selector used to walk up the element’s ancestry. ‣ Event.findElement(e, 'a')
  126. element or findElement? • Event.element returns the element the event

    fired on (IE: source. W3: target) ‣ Sometimes you’re not interested in it: perhaps bubbling means your registered ‘click’ on an a, and this is a span inside it... • Event.findElement lets you specify a CSS3 selector used to walk up the element’s ancestry. ‣ Event.findElement(e, 'a') ‣ Event.findElement(e, 'p.container')
  127. The art of stopping

  128. The art of stopping • Common pitfall: not caching handlers

  129. The art of stopping • Common pitfall: not caching handlers

    ‣ You just bind on the fly...
  130. The art of stopping • Common pitfall: not caching handlers

    ‣ You just bind on the fly... ‣ ...and deregistering doesn’t work!
  131. The art of stopping • Common pitfall: not caching handlers

    ‣ You just bind on the fly... ‣ ...and deregistering doesn’t work! Event.observe(this.element, 'click', this.clickHandler.bind(this)); ... Event.stopObserving(this.element, 'click', this.clickHandler.bind(this)); // But the listener still works!
  132. The art of stopping • Common pitfall: not caching handlers

    ‣ You just bind on the fly... ‣ ...and deregistering doesn’t work! • That’s because bind returns a new function Event.observe(this.element, 'click', this.clickHandler.bind(this)); ... Event.stopObserving(this.element, 'click', this.clickHandler.bind(this)); // But the listener still works!
  133. The art of stopping • Common pitfall: not caching handlers

    ‣ You just bind on the fly... ‣ ...and deregistering doesn’t work! • That’s because bind returns a new function ‣ Cache the bound listener Event.observe(this.element, 'click', this.clickHandler.bind(this)); ... Event.stopObserving(this.element, 'click', this.clickHandler.bind(this)); // But the listener still works!
  134. The art of stopping • Common pitfall: not caching handlers

    ‣ You just bind on the fly... ‣ ...and deregistering doesn’t work! • That’s because bind returns a new function ‣ Cache the bound listener Event.observe(this.element, 'click', this.clickHandler.bind(this)); ... Event.stopObserving(this.element, 'click', this.clickHandler.bind(this)); // But the listener still works! this._boundClickHandler = this.clickHandler.bind(this); Element.observe(this.element, 'click', this._boundClickHandler);
  135. JSON-Fu

  136. Converting stuff to JSON

  137. Converting stuff to JSON • Custom toJSON method

  138. Converting stuff to JSON • Custom toJSON method ‣ Array,

    Number, String, Date
  139. Converting stuff to JSON • Custom toJSON method ‣ Array,

    Number, String, Date • Generic Object.toJSON otherwise
  140. Converting stuff to JSON • Custom toJSON method ‣ Array,

    Number, String, Date • Generic Object.toJSON otherwise • So you’re good to go on any kind of data
  141. Converting stuff to JSON • Custom toJSON method ‣ Array,

    Number, String, Date • Generic Object.toJSON otherwise • So you’re good to go on any kind of data var obj = { name: 'Christophe', age: 29, talks: ['IP', 'AP', 'Sc'] }; Object.toJSON(obj) // ‘{"name": "Christophe", "age": 29, "talks": ["IP", "AP", "Sc"]}’
  142. Parsing JSON

  143. Parsing JSON • ‘yourJSONString’.evalJSON()

  144. Parsing JSON • ‘yourJSONString’.evalJSON() ‣ Wraps it in parentheses, just

    in case
  145. Parsing JSON • ‘yourJSONString’.evalJSON() ‣ Wraps it in parentheses, just

    in case • Security!
  146. Parsing JSON • ‘yourJSONString’.evalJSON() ‣ Wraps it in parentheses, just

    in case • Security! ‣ Text can be surrounded by a security wrapper text (Prototype.JSONFilter) to prevent accidental evaluation
  147. Parsing JSON • ‘yourJSONString’.evalJSON() ‣ Wraps it in parentheses, just

    in case • Security! ‣ Text can be surrounded by a security wrapper text (Prototype.JSONFilter) to prevent accidental evaluation ‣ evalJSON(true) does a sanity check on the text using isJSON()
  148. AJAX & JSON

  149. AJAX & JSON • The X-JSON header: just for tiny

    bits!
  150. AJAX & JSON • The X-JSON header: just for tiny

    bits! ‣ Passed as last argument to callbacks
  151. AJAX & JSON • The X-JSON header: just for tiny

    bits! ‣ Passed as last argument to callbacks ‣ Obtained internally by calling Ajax.Request’s evalJSON()
  152. AJAX & JSON • The X-JSON header: just for tiny

    bits! ‣ Passed as last argument to callbacks ‣ Obtained internally by calling Ajax.Request’s evalJSON() • JSON response body: 1.5.1.1 not there yet
  153. AJAX & JSON • The X-JSON header: just for tiny

    bits! ‣ Passed as last argument to callbacks ‣ Obtained internally by calling Ajax.Request’s evalJSON() • JSON response body: 1.5.1.1 not there yet ‣ Filters out the security wrapper, if any
  154. AJAX & JSON • The X-JSON header: just for tiny

    bits! ‣ Passed as last argument to callbacks ‣ Obtained internally by calling Ajax.Request’s evalJSON() • JSON response body: 1.5.1.1 not there yet ‣ Filters out the security wrapper, if any ‣ Requires a JavaScript MIME type, as usual
  155. AJAX & JSON • The X-JSON header: just for tiny

    bits! ‣ Passed as last argument to callbacks ‣ Obtained internally by calling Ajax.Request’s evalJSON() • JSON response body: 1.5.1.1 not there yet ‣ Filters out the security wrapper, if any ‣ Requires a JavaScript MIME type, as usual ‣ But an unstored JSON structure is useless!
  156. DOM-Fu

  157. Element.extend a closer look

  158. Element.extend a closer look • Continually optimized

  159. Element.extend a closer look • Continually optimized • Any element

    is extended at most once
  160. Element.extend a closer look • Continually optimized • Any element

    is extended at most once • Done internally by $, which is used throughout the library
  161. Element.extend a closer look • Continually optimized • Any element

    is extended at most once • Done internally by $, which is used throughout the library ‣ Returned elements / element sets: extended
  162. Element.extend a closer look • Continually optimized • Any element

    is extended at most once • Done internally by $, which is used throughout the library ‣ Returned elements / element sets: extended • Adds Element.Methods.* (~60 methods)
  163. Element.extend a closer look • Continually optimized • Any element

    is extended at most once • Done internally by $, which is used throughout the library ‣ Returned elements / element sets: extended • Adds Element.Methods.* (~60 methods) ‣ And tag-specific (Element.Methods.ByTag)
  164. Actual extension cost HTMLElement. prototype HTML*Element. prototype

  165. Actual extension cost HTMLElement. prototype HTML*Element. prototype Linear Linear

  166. Actual extension cost HTMLElement. prototype HTML*Element. prototype Linear Linear Const.

    Linear
  167. Actual extension cost HTMLElement. prototype HTML*Element. prototype Linear Linear Const.

    Linear Const. Const.
  168. Actual extension cost HTMLElement. prototype HTML*Element. prototype Linear Linear Const.

    Linear Const. Const. Const. Linear
  169. Actual extension cost HTMLElement. prototype HTML*Element. prototype Linear Linear Const.

    Linear Const. Const. Const. Linear Const. Const.
  170. Actual extension cost ‣ The danger of slowness looms only

    on MSIE, if Element.Methods grows too large HTMLElement. prototype HTML*Element. prototype Linear Linear Const. Linear Const. Const. Const. Linear Const. Const.
  171. Actual extension cost ‣ The danger of slowness looms only

    on MSIE, if Element.Methods grows too large ‣ Even on MSIE, elements are extended lazily only the first time: after that, they’re good HTMLElement. prototype HTML*Element. prototype Linear Linear Const. Linear Const. Const. Const. Linear Const. Const.
  172. Those few “static” methods

  173. Those few “static” methods • Noticed a few methods are

    not in X.Methods, but right in X?
  174. Those few “static” methods • Noticed a few methods are

    not in X.Methods, but right in X? ‣ Form.reset
  175. Those few “static” methods • Noticed a few methods are

    not in X.Methods, but right in X? ‣ Form.reset ‣ Field.focus, Field.select
  176. Those few “static” methods • Noticed a few methods are

    not in X.Methods, but right in X? ‣ Form.reset ‣ Field.focus, Field.select • That’s because native methods exist
  177. Those few “static” methods • Noticed a few methods are

    not in X.Methods, but right in X? ‣ Form.reset ‣ Field.focus, Field.select • That’s because native methods exist ‣ Ours just return their argument afterwards
  178. Those few “static” methods • Noticed a few methods are

    not in X.Methods, but right in X? ‣ Form.reset ‣ Field.focus, Field.select • That’s because native methods exist ‣ Ours just return their argument afterwards ‣ Consistent with custom methods (chaining!)
  179. Element.addMethods

  180. Element.addMethods • Essentially updates the extension mechanism from the method

    repositories
  181. Element.addMethods • Essentially updates the extension mechanism from the method

    repositories ‣ Updates HTML element prototypes
  182. Element.addMethods • Essentially updates the extension mechanism from the method

    repositories ‣ Updates HTML element prototypes ‣ Deals with “simulated” methods (those that compensate for missing W3C DOM ones)
  183. Element.addMethods • Essentially updates the extension mechanism from the method

    repositories ‣ Updates HTML element prototypes ‣ Deals with “simulated” methods (those that compensate for missing W3C DOM ones) ‣ Deals with quirks across browsers
  184. Element.addMethods • Essentially updates the extension mechanism from the method

    repositories ‣ Updates HTML element prototypes ‣ Deals with “simulated” methods (those that compensate for missing W3C DOM ones) ‣ Deals with quirks across browsers • Adding your custom extensions?
  185. Element.addMethods • Essentially updates the extension mechanism from the method

    repositories ‣ Updates HTML element prototypes ‣ Deals with “simulated” methods (those that compensate for missing W3C DOM ones) ‣ Deals with quirks across browsers • Adding your custom extensions? ‣ Call it once you’re done
  186. Custom DOM extension

  187. Custom DOM extension MyLib.Methods = { catchEye: function(element) { Element.addClassName('eyeCatcher');

    Element.visualEffect(element, 'pulsate', { duration: 2 }); return element; } }; MyLib.FieldMethods = { toggleError: function(element, cleared) { var meth = (cleared ? 'remove' : 'add') + 'ClassName'; Element[meth](element, 'erroneous'); return element; } };
  188. Custom DOM extension MyLib.Methods = { catchEye: function(element) { Element.addClassName('eyeCatcher');

    Element.visualEffect(element, 'pulsate', { duration: 2 }); return element; } }; MyLib.FieldMethods = { toggleError: function(element, cleared) { var meth = (cleared ? 'remove' : 'add') + 'ClassName'; Element[meth](element, 'erroneous'); return element; } }; Object.extend(Element.Methods, MyLib.Methods);
  189. Custom DOM extension MyLib.Methods = { catchEye: function(element) { Element.addClassName('eyeCatcher');

    Element.visualEffect(element, 'pulsate', { duration: 2 }); return element; } }; MyLib.FieldMethods = { toggleError: function(element, cleared) { var meth = (cleared ? 'remove' : 'add') + 'ClassName'; Element[meth](element, 'erroneous'); return element; } }; Object.extend(Element.Methods, MyLib.Methods); Object.extend(Form.Element.Methods, MyLib.FieldMethods);
  190. Custom DOM extension MyLib.Methods = { catchEye: function(element) { Element.addClassName('eyeCatcher');

    Element.visualEffect(element, 'pulsate', { duration: 2 }); return element; } }; MyLib.FieldMethods = { toggleError: function(element, cleared) { var meth = (cleared ? 'remove' : 'add') + 'ClassName'; Element[meth](element, 'erroneous'); return element; } }; Object.extend(Element.Methods, MyLib.Methods); Object.extend(Form.Element.Methods, MyLib.FieldMethods); Element.addMethods();
  191. Class-Fu

  192. Classes in JavaScript • In JavaScript, a type is defined

    by it constructor, which is just a function. • “Static” properties / methods are defined directly on the constructor object • Instance properties / methods are defined on the constructor’s prototype • Let’s leave private/privileged/public alone...
  193. Classes in Prototype

  194. Classes in Prototype • Warning: will evolve big time in

    2.0
  195. Classes in Prototype • Warning: will evolve big time in

    2.0 • Prototype just defines a Class.create function that provides a constructor which automatically calls the new instance’s initialize method (forwarding arg’s).
  196. Classes in Prototype • Warning: will evolve big time in

    2.0 • Prototype just defines a Class.create function that provides a constructor which automatically calls the new instance’s initialize method (forwarding arg’s). • Lets you put initialization code right beside instance methods / properties.
  197. Creating a class AsyncLoader = Class.create(); AsyncLoader.prototype = { resources:

    {}, initialize: function() { var args = $A(arguments), callback; if ('string' != typeof args.last()) callback = args.pop().bind(this); args.each(function(uri) { new Ajax.Request(uri, { method: 'get', onSuccess: function(xhr) { this.resources[uri] = xhr.responseText; (callback || Prototype.emptyFunction)(uri); }}); }); } }; function onURILoaded(uri) { ... } new AsyncLoader('foo.html', 'bar.php', onURILoaded);
  198. What’s Object.extend for?

  199. What’s Object.extend for? • It simply copies over all properties

    of a source object into a destination object.
  200. What’s Object.extend for? • It simply copies over all properties

    of a source object into a destination object. • Prototype uses it all over the place to...
  201. What’s Object.extend for? • It simply copies over all properties

    of a source object into a destination object. • Prototype uses it all over the place to... ‣ Build option sets out of defaults + actual
  202. What’s Object.extend for? • It simply copies over all properties

    of a source object into a destination object. • Prototype uses it all over the place to... ‣ Build option sets out of defaults + actual
  203. What’s Object.extend for? • It simply copies over all properties

    of a source object into a destination object. • Prototype uses it all over the place to... ‣ Build option sets out of defaults + actual this.options = Object.clone(MyClass.DefaultOptions); Object.extend(this.options, options || {});
  204. What’s Object.extend for? • It simply copies over all properties

    of a source object into a destination object. • Prototype uses it all over the place to... ‣ Build option sets out of defaults + actual ‣ Mix in modules with classes this.options = Object.clone(MyClass.DefaultOptions); Object.extend(this.options, options || {});
  205. What’s Object.extend for? • It simply copies over all properties

    of a source object into a destination object. • Prototype uses it all over the place to... ‣ Build option sets out of defaults + actual ‣ Mix in modules with classes this.options = Object.clone(MyClass.DefaultOptions); Object.extend(this.options, options || {}); MyContainer = Class.create(); Object.extend(MyContainer.prototype, Enumerable); Object.extend(MyContainer.prototype, { // initialize, _each, etc. });
  206. Static Object methods

  207. Static Object methods • We don’t augment Object.prototype

  208. Static Object methods • We don’t augment Object.prototype ‣ We

    used to. Plain rude, pollutes everyone.
  209. Static Object methods • We don’t augment Object.prototype ‣ We

    used to. Plain rude, pollutes everyone. • We do provide utility methods as static
  210. Static Object methods • We don’t augment Object.prototype ‣ We

    used to. Plain rude, pollutes everyone. • We do provide utility methods as static ‣ Object.method(obj)
  211. Static Object methods • We don’t augment Object.prototype ‣ We

    used to. Plain rude, pollutes everyone. • We do provide utility methods as static ‣ Object.method(obj) ‣ clone, extend, keys, values: simple hashes!
  212. Static Object methods • We don’t augment Object.prototype ‣ We

    used to. Plain rude, pollutes everyone. • We do provide utility methods as static ‣ Object.method(obj) ‣ clone, extend, keys, values: simple hashes! ‣ toJSON: we saw it already.
  213. Static Object methods • We don’t augment Object.prototype ‣ We

    used to. Plain rude, pollutes everyone. • We do provide utility methods as static ‣ Object.method(obj) ‣ clone, extend, keys, values: simple hashes! ‣ toJSON: we saw it already. ‣ inspect: debug-oriented string representation
  214. The Future Is Coming

  215. Event overhaul • Event object wrapping • Unified DOMContentLoaded •

    Scope correction (this = element) • Custom events
  216. AJAX overhaul • Ajax.Response • Extended JSON support ‣ JSON

    as response type, etc.
  217. Templating, revisited • Dot notation: #{person.name} • Square brackets: #{people[0]},

    #{['']} • String#interpolate
  218. Miscellanea • Relying on native JS methods for Array whenever

    possible (e.g. forEach) • Extended grep semantics • Dynamic content beef-up (toHTML, toElement, replace/update, isElement...) • Bug fixes and performance boosts across the board
  219. Online resources

  220. Online resources • The excellent documentation site ‣ http://prototypejs.org ‣

    API reference, tutorials, downloads, blog... ‣ Soon: 3rd-party libs roster
  221. Online resources • The excellent documentation site ‣ http://prototypejs.org ‣

    API reference, tutorials, downloads, blog... ‣ Soon: 3rd-party libs roster • The official lists ‣ http://groups.google.com/rubyonrails-spinoffs ‣ http://groups.google.com/prototype-core
  222. Online resources • The excellent documentation site ‣ http://prototypejs.org ‣

    API reference, tutorials, downloads, blog... ‣ Soon: 3rd-party libs roster • The official lists ‣ http://groups.google.com/rubyonrails-spinoffs ‣ http://groups.google.com/prototype-core • IRC ‣ #prototype on Freenode
  223. Shameless plug

  224. Shameless plug

  225. Shameless plug • “The Bungee Book”

  226. Shameless plug • “The Bungee Book” • Available already (as

    beta) from the Pragmatic Bookshelf ‣ http://books.pragprog.com/titles/cppsu/
  227. Shameless plug • “The Bungee Book” • Available already (as

    beta) from the Pragmatic Bookshelf ‣ http://books.pragprog.com/titles/cppsu/ • 95% content-complete already
  228. Shameless plug • “The Bungee Book” • Available already (as

    beta) from the Pragmatic Bookshelf ‣ http://books.pragprog.com/titles/cppsu/ • 95% content-complete already • Up-to-date on the latest stuff
  229. Shameless plug • “The Bungee Book” • Available already (as

    beta) from the Pragmatic Bookshelf ‣ http://books.pragprog.com/titles/cppsu/ • 95% content-complete already • Up-to-date on the latest stuff • Pre-order the paper book too!
  230. Q&A

  231. Q&A What Say You?