Slide 1

Slide 1 text

Advanced Prototype Christophe Porteneuve The Ajax Experience · San Francisco, July 25 2007

Slide 2

Slide 2 text

Did you attend Intro?

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

“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

Slide 15

Slide 15 text

“Advanced,” like, how?

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

Should I know Prototype a bit already?

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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.

Slide 26

Slide 26 text

Function-Fu

Slide 27

Slide 27 text

Method scopes (and other horror stories)

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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"

Slide 31

Slide 31 text

What the hell just happened?

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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?

Slide 35

Slide 35 text

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!

Slide 36

Slide 36 text

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?

Slide 37

Slide 37 text

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!

Slide 38

Slide 38 text

One call to bind them all

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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)

Slide 42

Slide 42 text

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.

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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'

Slide 52

Slide 52 text

Binding event listeners

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

Binding or closure?

Slide 58

Slide 58 text

Binding or closure? • Think twice before binding inner functions

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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:

Slide 62

Slide 62 text

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!

Slide 63

Slide 63 text

Composing with wrap

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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!

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

Methodizing

Slide 69

Slide 69 text

Methodizing • Yuck, sounds like a Bubble 1.0 slide!

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

Enumerable-Fu

Slide 76

Slide 76 text

To each his own

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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!

Slide 81

Slide 81 text

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!

Slide 82

Slide 82 text

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!

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

map / collect

Slide 85

Slide 85 text

map / collect • The generic transformation function

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

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'

Slide 92

Slide 92 text

faster map: pluck

Slide 93

Slide 93 text

faster map: pluck • Common map use-case

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

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

Slide 97

Slide 97 text

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]

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

faster map: invoke

Slide 101

Slide 101 text

faster map: invoke • Common map use-case

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

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

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

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'

Slide 108

Slide 108 text

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

Slide 109

Slide 109 text

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'

Slide 110

Slide 110 text

A rundown of the rest...

Slide 111

Slide 111 text

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

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

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

Slide 114

Slide 114 text

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

Slide 115

Slide 115 text

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

Slide 116

Slide 116 text

Event-Fu

Slide 117

Slide 117 text

Save handlers: use bubbling!

Slide 118

Slide 118 text

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

Slide 119

Slide 119 text

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

Slide 120

Slide 120 text

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

Slide 121

Slide 121 text

element or findElement?

Slide 122

Slide 122 text

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

Slide 123

Slide 123 text

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

Slide 124

Slide 124 text

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.

Slide 125

Slide 125 text

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

Slide 126

Slide 126 text

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

Slide 127

Slide 127 text

The art of stopping

Slide 128

Slide 128 text

The art of stopping • Common pitfall: not caching handlers

Slide 129

Slide 129 text

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

Slide 130

Slide 130 text

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

Slide 131

Slide 131 text

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!

Slide 132

Slide 132 text

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!

Slide 133

Slide 133 text

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!

Slide 134

Slide 134 text

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

Slide 135

Slide 135 text

JSON-Fu

Slide 136

Slide 136 text

Converting stuff to JSON

Slide 137

Slide 137 text

Converting stuff to JSON • Custom toJSON method

Slide 138

Slide 138 text

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

Slide 139

Slide 139 text

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

Slide 140

Slide 140 text

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

Slide 141

Slide 141 text

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

Slide 142

Slide 142 text

Parsing JSON

Slide 143

Slide 143 text

Parsing JSON • ‘yourJSONString’.evalJSON()

Slide 144

Slide 144 text

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

Slide 145

Slide 145 text

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

Slide 146

Slide 146 text

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

Slide 147

Slide 147 text

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

Slide 148

Slide 148 text

AJAX & JSON

Slide 149

Slide 149 text

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

Slide 150

Slide 150 text

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

Slide 151

Slide 151 text

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

Slide 152

Slide 152 text

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

Slide 153

Slide 153 text

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

Slide 154

Slide 154 text

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

Slide 155

Slide 155 text

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!

Slide 156

Slide 156 text

DOM-Fu

Slide 157

Slide 157 text

Element.extend a closer look

Slide 158

Slide 158 text

Element.extend a closer look • Continually optimized

Slide 159

Slide 159 text

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

Slide 160

Slide 160 text

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

Slide 161

Slide 161 text

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

Slide 162

Slide 162 text

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)

Slide 163

Slide 163 text

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)

Slide 164

Slide 164 text

Actual extension cost HTMLElement. prototype HTML*Element. prototype

Slide 165

Slide 165 text

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

Slide 166

Slide 166 text

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

Slide 167

Slide 167 text

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

Slide 168

Slide 168 text

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

Slide 169

Slide 169 text

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

Slide 170

Slide 170 text

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.

Slide 171

Slide 171 text

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.

Slide 172

Slide 172 text

Those few “static” methods

Slide 173

Slide 173 text

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

Slide 174

Slide 174 text

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

Slide 175

Slide 175 text

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

Slide 176

Slide 176 text

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

Slide 177

Slide 177 text

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

Slide 178

Slide 178 text

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

Slide 179

Slide 179 text

Element.addMethods

Slide 180

Slide 180 text

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

Slide 181

Slide 181 text

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

Slide 182

Slide 182 text

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)

Slide 183

Slide 183 text

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

Slide 184

Slide 184 text

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?

Slide 185

Slide 185 text

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

Slide 186

Slide 186 text

Custom DOM extension

Slide 187

Slide 187 text

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

Slide 188

Slide 188 text

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

Slide 189

Slide 189 text

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

Slide 190

Slide 190 text

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

Slide 191

Slide 191 text

Class-Fu

Slide 192

Slide 192 text

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

Slide 193

Slide 193 text

Classes in Prototype

Slide 194

Slide 194 text

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

Slide 195

Slide 195 text

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

Slide 196

Slide 196 text

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.

Slide 197

Slide 197 text

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

Slide 198

Slide 198 text

What’s Object.extend for?

Slide 199

Slide 199 text

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

Slide 200

Slide 200 text

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

Slide 201

Slide 201 text

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

Slide 202

Slide 202 text

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

Slide 203

Slide 203 text

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

Slide 204

Slide 204 text

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

Slide 205

Slide 205 text

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

Slide 206

Slide 206 text

Static Object methods

Slide 207

Slide 207 text

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

Slide 208

Slide 208 text

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

Slide 209

Slide 209 text

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

Slide 210

Slide 210 text

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)

Slide 211

Slide 211 text

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!

Slide 212

Slide 212 text

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.

Slide 213

Slide 213 text

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

Slide 214

Slide 214 text

The Future Is Coming

Slide 215

Slide 215 text

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

Slide 216

Slide 216 text

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

Slide 217

Slide 217 text

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

Slide 218

Slide 218 text

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

Slide 219

Slide 219 text

Online resources

Slide 220

Slide 220 text

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

Slide 221

Slide 221 text

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

Slide 222

Slide 222 text

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

Slide 223

Slide 223 text

Shameless plug

Slide 224

Slide 224 text

Shameless plug

Slide 225

Slide 225 text

Shameless plug • “The Bungee Book”

Slide 226

Slide 226 text

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

Slide 227

Slide 227 text

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

Slide 228

Slide 228 text

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

Slide 229

Slide 229 text

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!

Slide 230

Slide 230 text

Q&A

Slide 231

Slide 231 text

Q&A What Say You?