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

Advanced Prototype.js

Advanced Prototype.js

Christophe Porteneuve

July 25, 2007
Tweet

More Decks by Christophe Porteneuve

Other Decks in Technology

Transcript

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

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

    We saw... ‣ A few noteworthy JavaScript aspects ‣ Extensions to native types (arrays, strings...)
  3. 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
  4. 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
  5. 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
  6. “So you’re qualified cuz...?” • Prototype Core member • Rails

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

    & script.aculo.us contributor • Prototype doc writer (prototypejs.org) • Prototype book author
  8. “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
  9. “Advanced,” like, how? • Function binding, wrapping & methodizing •

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

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

    Enumerable best practices • Event best practices • JSON love • Custom DOM extensions, custom classes
  12. “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!
  13. Should I know Prototype a bit already? • Well... yeah.

    It would help. • We won’t go over each syntax and basic class here.
  14. 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.
  15. 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()); }
  16. 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);
  17. 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"
  18. What the hell just happened? • We passed a reference

    to a method ‣ It lost its binding: the scope to which “this” refers.
  19. 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?
  20. 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!
  21. 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?
  22. 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!
  23. One call to bind them all • bind “attaches” a

    scope object to a method ‣ Actually returns an ad hoc anonymous function
  24. 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)
  25. 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.
  26. 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))
  27. 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!"
  28. 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);
  29. 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...
  30. But wait! There’s more! • Most people don’t realize this,

    but bind lets you pre- fill arguments, too!
  31. 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(','); } }
  32. 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);
  33. 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();
  34. 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'
  35. 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...
  36. 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
  37. 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);
  38. Binding or closure? • Think twice before binding inner functions

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

    ‣ Wrapping anonymous function: calls × 2 ‣ In a fat loop: feel the pain?
  40. 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:
  41. 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!
  42. Composing with wrap • Composing a function into another: f(g(x))

    • Your outer function must expect its inner function as its first argument.
  43. 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!
  44. 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');
  45. Methodizing • Yuck, sounds like a Bubble 1.0 slide! •

    Encodes the following pattern: ‣ A function takes its subject as first argument
  46. 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
  47. 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
  48. 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);
  49. To each his own • Are you one of the

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

    many who over-use each? ‣ each is the generic, all-purpose iterator
  51. 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
  52. 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!
  53. 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!
  54. 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!
  55. 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
  56. map / collect • The generic transformation function ‣ Each

    element is turned into a derived value thanks to a custom transform you provide
  57. 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
  58. 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); })
  59. 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); }) // ['*', '**', '***', '****', '*****']
  60. 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(' ');
  61. 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'
  62. faster map: pluck • Common map use-case • Simple transform:

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

    fetching the same property for each element • Much faster than a map
  64. 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')
  65. 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]
  66. 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('')
  67. 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' ;-)
  68. faster map: invoke • Common map use-case • Simple transform:

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

    calling the same method, with the same arguments • Much faster than a map
  70. 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
  71. 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
  72. 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('')
  73. 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'
  74. 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')
  75. 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'
  76. A rundown of the rest... • eachSlice, inGroupsOf: cutting in

    slots • select, reject, partition, grep, all, any: global lookups, checks & filters
  77. 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
  78. 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
  79. 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...
  80. Save handlers: use bubbling! • The Ugly: multiple bind[AsEventListener] on

    the same handler elements.each(function(e) { e.observe('click', this.handleClick.bind(this)); });
  81. 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));
  82. 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);
  83. 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...
  84. 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.
  85. 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')
  86. 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')
  87. The art of stopping • Common pitfall: not caching handlers

    ‣ You just bind on the fly... ‣ ...and deregistering doesn’t work!
  88. 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!
  89. 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!
  90. 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!
  91. 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);
  92. Converting stuff to JSON • Custom toJSON method ‣ Array,

    Number, String, Date • Generic Object.toJSON otherwise
  93. 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
  94. 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"]}’
  95. 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
  96. 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()
  97. AJAX & JSON • The X-JSON header: just for tiny

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

    bits! ‣ Passed as last argument to callbacks ‣ Obtained internally by calling Ajax.Request’s evalJSON()
  99. 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
  100. 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
  101. 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
  102. 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!
  103. Element.extend a closer look • Continually optimized • Any element

    is extended at most once • Done internally by $, which is used throughout the library
  104. 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
  105. 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)
  106. 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)
  107. 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.
  108. 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.
  109. Those few “static” methods • Noticed a few methods are

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

    not in X.Methods, but right in X? ‣ Form.reset ‣ Field.focus, Field.select
  111. 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
  112. 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
  113. 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!)
  114. 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)
  115. 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
  116. 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?
  117. 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
  118. 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; } };
  119. 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);
  120. 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);
  121. 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();
  122. 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...
  123. 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).
  124. 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.
  125. 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);
  126. What’s Object.extend for? • It simply copies over all properties

    of a source object into a destination object.
  127. 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...
  128. 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
  129. 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
  130. 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 || {});
  131. 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 || {});
  132. 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. });
  133. Static Object methods • We don’t augment Object.prototype ‣ We

    used to. Plain rude, pollutes everyone. • We do provide utility methods as static
  134. 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)
  135. 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!
  136. 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.
  137. 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
  138. Event overhaul • Event object wrapping • Unified DOMContentLoaded •

    Scope correction (this = element) • Custom events
  139. 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
  140. Online resources • The excellent documentation site ‣ http://prototypejs.org ‣

    API reference, tutorials, downloads, blog... ‣ Soon: 3rd-party libs roster
  141. 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
  142. 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
  143. Shameless plug • “The Bungee Book” • Available already (as

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

    beta) from the Pragmatic Bookshelf ‣ http://books.pragprog.com/titles/cppsu/ • 95% content-complete already
  145. 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
  146. 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!
  147. Q&A