$30 off During Our Annual Pro Sale. View Details »

Advanced Prototype.js

Advanced Prototype.js

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

    View Slide

  2. Did you attend Intro?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  9. “So you’re qualified cuz...?”

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  15. “Advanced,” like, how?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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!

    View Slide

  22. Should I know Prototype a
    bit already?

    View Slide

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

    View Slide

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

    View Slide

  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.

    View Slide

  26. Function-Fu

    View Slide

  27. Method scopes
    (and other horror stories)

    View Slide

  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()); }

    View Slide

  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);

    View Slide

  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"

    View Slide

  31. What the hell just happened?

    View Slide

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

    View Slide

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

    View Slide

  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?

    View Slide

  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!

    View Slide

  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?

    View Slide

  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!

    View Slide

  38. One call to bind them all

    View Slide

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

    View Slide

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

    View Slide

  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)

    View Slide

  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.

    View Slide

  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))

    View Slide

  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!"

    View Slide

  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);

    View Slide

  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...

    View Slide

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

    View Slide

  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(',');
    }
    }

    View Slide

  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);

    View Slide

  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();

    View Slide

  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'

    View Slide

  52. Binding event listeners

    View Slide

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

    View Slide

  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...

    View Slide

  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

    View Slide

  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);

    View Slide

  57. Binding or closure?

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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:

    View Slide

  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!

    View Slide

  63. Composing with wrap

    View Slide

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

    View Slide

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

    View Slide

  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!

    View Slide

  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');

    View Slide

  68. Methodizing

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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);

    View Slide

  75. Enumerable-Fu

    View Slide

  76. To each his own

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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!

    View Slide

  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!

    View Slide

  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!

    View Slide

  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

    View Slide

  84. map / collect

    View Slide

  85. map / collect
    • The generic transformation function

    View Slide

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

    View Slide

  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

    View Slide

  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); })

    View Slide

  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); })
    // ['*', '**', '***', '****', '*****']

    View Slide

  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(' ');

    View Slide

  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'

    View Slide

  92. faster map: pluck

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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')

    View Slide

  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]

    View Slide

  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('')

    View Slide

  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' ;-)

    View Slide

  100. faster map: invoke

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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('')

    View Slide

  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'

    View Slide

  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')

    View Slide

  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'

    View Slide

  110. A rundown of the rest...

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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...

    View Slide

  116. Event-Fu

    View Slide

  117. Save handlers: use bubbling!

    View Slide

  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));
    });

    View Slide

  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));

    View Slide

  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);

    View Slide

  121. element or findElement?

    View Slide

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

    View Slide

  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...

    View Slide

  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.

    View Slide

  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')

    View Slide

  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')

    View Slide

  127. The art of stopping

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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!

    View Slide

  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!

    View Slide

  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!

    View Slide

  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);

    View Slide

  135. JSON-Fu

    View Slide

  136. Converting stuff to JSON

    View Slide

  137. Converting stuff to JSON
    • Custom toJSON method

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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"]}’

    View Slide

  142. Parsing JSON

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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()

    View Slide

  148. AJAX & JSON

    View Slide

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

    View Slide

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

    View Slide

  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()

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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!

    View Slide

  156. DOM-Fu

    View Slide

  157. Element.extend
    a closer look

    View Slide

  158. Element.extend
    a closer look
    • Continually optimized

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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)

    View Slide

  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)

    View Slide

  164. Actual extension cost
    HTMLElement.
    prototype
    HTML*Element.
    prototype

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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.

    View Slide

  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.

    View Slide

  172. Those few “static”
    methods

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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!)

    View Slide

  179. Element.addMethods

    View Slide

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

    View Slide

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

    View Slide

  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)

    View Slide

  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

    View Slide

  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?

    View Slide

  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

    View Slide

  186. Custom DOM extension

    View Slide

  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;
    }
    };

    View Slide

  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);

    View Slide

  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);

    View Slide

  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();

    View Slide

  191. Class-Fu

    View Slide

  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...

    View Slide

  193. Classes in Prototype

    View Slide

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

    View Slide

  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).

    View Slide

  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.

    View Slide

  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);

    View Slide

  198. What’s Object.extend for?

    View Slide

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

    View Slide

  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...

    View Slide

  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

    View Slide

  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

    View Slide

  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 || {});

    View Slide

  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 || {});

    View Slide

  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.
    });

    View Slide

  206. Static Object methods

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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)

    View Slide

  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!

    View Slide

  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.

    View Slide

  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

    View Slide

  214. The Future Is Coming

    View Slide

  215. Event overhaul
    • Event object wrapping
    • Unified DOMContentLoaded
    • Scope correction (this = element)
    • Custom events

    View Slide

  216. AJAX overhaul
    • Ajax.Response
    • Extended JSON support
    ‣ JSON as response type, etc.

    View Slide

  217. Templating, revisited
    • Dot notation: #{person.name}
    • Square brackets: #{people[0]}, #{['']}
    • String#interpolate

    View Slide

  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

    View Slide

  219. Online resources

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  223. Shameless plug

    View Slide

  224. Shameless plug

    View Slide

  225. Shameless plug
    • “The Bungee Book”

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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!

    View Slide

  230. Q&A

    View Slide

  231. Q&A
    What Say You?

    View Slide