9. May 9th, 2005 Http://www.ïŹickr.com/photos/nataliedowne/14517351/
(me, in 2005)
10. Also in 2005
†Gmail had its ïŹrst birthday, still in invite-only beta
†Google Maps launched February 8th
†The term âAjaxâ was coined February 18th by Jesse James Garrett
†Ajaxian.com launched March 10th
11. A flurry of library activity
†February 2005: First Prototype.js
†March 2005: First public MochiKit code
†YUI started development internally at Yahoo! in 2005 (ïŹrst public
release was February 2006)
†jQuery released at BarCamp Boston in January 2006
20. Everything in JavaScript is an object
Strings and numbers
> "A string".length
8
> 123.toString()
SyntaxError: Unexpected token ILLEGAL
> (123).toString()
â123â
Even functions:
> function hello() { alert("hello") }
> hello.toString()
"function hello() { alert("hello") }"
21. You can make your own objects
// The same thing:
var simon = new Object();
var simon = {};
// Also the same:
simon.name = "Simon Willison"; // name is a property
simon["name"] = "Simon Willison";
// Object literal syntax is most useful:
var simon = {
name: "Simon Willison",
age: 29
};
22. You can loop through properties
> var simon = {
name: "Simon Willison",
age: 29
};
> for (var prop in simon) {
console.log(prop + ': ' + simon[prop]);
}
name: Simon
age: 29
(more on this later...)
23. (almost) Everything in JavaScript is a
property on an object
> parseInt("100 bunnies");
100
> parseInt === window.parseInt // window is the global object
true
> window === window.window
true
> window === window.window.window
true
> true === window.true
SyntaxError: Unexpected token true
24. Arrays
> var a = new Array(); // Old-school
> var a = []; // Literal syntax
> var a = ["dog", "cat", "chicken"];
> a.length;
3
> a[0]
"dog"
> a[2]
"chicken"
> a[3]
undefined
25. Iteration through arrays
> var a = ["dog", "cat", "chicken"];
> for (var i = 0; i < a.length; i++) {
console.log(a[i]);
}
dog
cat
chicken
26. Tricksy array iteration
> var a = ["dog", "cat", "chicken"];
> for (var i = 0, item; item = a[i]; i++) {
console.log(item);
}
dog
cat
chicken
// But watch out for falsey values:
> var a = [123, 0, 12, 443];
> for (var i = 0, item; item = a[i]; i++) { console.log(item); }
123
29. Functions
// What could be simpler?
function addTwoNumbers(a, b) {
var total = a + b; // A local variable
return total;
}
> addTwoNumbers(2, 4)
6
30. Functions
// What could be simpler?
function addTwoNumbers(a, b) {
var total = a + b; // A local variable
return total;
}
> addTwoNumbers(2, 4)
6
> addTwoNumbers()
NaN
31. Functions
// What could be simpler?
function addTwoNumbers(a, b) {
var total = a + b; // A local variable
return total;
}
> addTwoNumbers(2, 4)
6
> addTwoNumbers()
NaN
> addTwoNumbers(2, 4, 8)
6
32. Function parameters are more
like guidelines
// arguments is a magic array-like object
function add() {
var sum = 0;
for (var i = 0, j = arguments.length; i < j; i++) {
sum += arguments[i];
}
return sum;
}
> add(1, 3, 4, 5, 0, 5);
18
33. Anonymous functions
var add = function() {
var sum = 0;
for (var i = 0, j = arguments.length; i < j; i++) {
sum += arguments[i];
}
return sum;
}
var added = (function() {
var a = 2, b = 5; // Local variables
return a + b;
})();
34. Modern array iteration
> var a = ["dog", "cat"];
> a.forEach(function(item) {
console.log(item);
}
dog
cat
> a.forEach(function(item, index) {
console.log(index + ': ' + item);
}
0: dog
1: cat
35. Closures
function makeOp(op, x) {
switch(op) {
case '+':
return function(y) { return y + x };
case '-':
return function(y) { return y - x };
case '/':
return function(y) { return y / x };
case '*':
return function(y) { return y * x };
}
}
> var third = makeOp('/', 3);
> var dbl = makeOp('*', 2);
> console.log(third(12) + ' ' + dbl(8));
4 16
36. How does this work?
†Remember âeverything in JavaScript is a property of an objectâ?
†Imagine that local variables belong to a âlocal scopeâ object, which
gets created when a function is executed
†Now imagine this object can stick around after the function has
ïŹnished executing
†A closure is a function plus the scope in which that function was
created
†Since closures capture state, you can use them as a kind of object
37. Real-world closure example
function revealer(el, duration) {
return function(ev) {
ev.preventDefault();
el.show(duration);
}
}
$("#mylink').click(revealer($('#panel'), 500);
$("#mylink2').click(revealer($('#panel2'), 1000);
38. Functions and objects
function makePerson(first, last) {
return {
"first": first,
"last": last
}
}
function personFullName(person) {
return person.first + ' ' + person.last;
}
> simon = makePerson("Simon", "Willison");
> personFullName(simon)
"Simon Willison"
39. First attempt at methods
function makePerson(first, last) {
return {
"first": first,
"last": last,
"fullName": function() {
return this.first + ' ' + this.last;
}
}
}
> simon = makePerson("Simon", "Willison");
> simon.fullName();
"Simon Willison"
40. What the heck is âthisâ?
†When you write:
> simon.fullName();
â€
fullName() is executed with this pointing to simon.
†If you call a method without using the '.' operator, this is set to the
global object, i.e. window.
> var fullNameMethod = simon.fullName;
> fullNameMethod();
undefined undefined
42. You can control what this is
> simon = makePerson("Simon", "Willison");
> nat = makePerson("Natalie", "Downe");
> nat.fullName();
"Natalie Downe"
> nat.fullName.call(simon);
"Simon Willison"
> simon.fullName.apply(nat);
"Natalie Downe"
43. call v.s. apply
†Call lets you specify both the value of this and the arguments that
should be passed:
†myFunction.call(myThis, arg1, arg2, arg3);
†Apply lets you do the same thing, but pass an array of arguments
instead:
†myFunction.apply(myThis, [arg1, arg2, arg3]);
†(I always have to look this up)
44. Constructors
function Person(first, last) {
this.first = first;
this.last = last;
this.fullName = function() {
return this.first + ' ' + this.last;
}
}
> var simon = new Person("Simon", "Willison");
> s.fullName();
"Simon Willison"
45. What does ânewâ do?
†var simon = new Person(first, last);
†Creates an empty object: {}
†Executes the Person function with this set to the new empty object
†Adds Person.prototype to the object's prototype chain
46. Prototype inheritance
The following is wasteful
function Person(first, last) {
this.first = first;
this.last = last;
this.fullName = function() {
return this.first + ' ' + this.last;
}
}
How can we avoid creating a fullName function for every object?
48. You can extend built-in classes
> "hello".reversed();
TypeError: Object hello has no method 'reversed'
> String.prototype.reversed = function() {
var r = '';
for (var i = this.length - 1; i >= 0; i--) {
r += this[i];
}
return r;
}
> "hello".reversed();
"olleh"
49. That doesnât mean you should
†Donât modify objects you donât own
†Extending Object.prototype breaks the (for var prop in obj) idiom
†Prototype.js added document.getElementsByClassName
†Then Mozilla added document.getElementsByClassName...
†The behaviour was slightly different, so code broke
†If youâd written your own Array.forEach() method, today your code
would be clashing with the new forEach() method in JavaScript 1.6
50. Prototype chain
var simon = new Person(...);
simon.toString();
Try simon.toString()
...
Try Person.prototype.toString()
...
Try Object.toString()
...
Give up
51. Advanced prototype chains
function Mammal() { ... }
Mammal.prototype.eatThings = ...
Person.prototype = new Mammal();
Person.prototype.learnToRead = ...
var simon = new Person(...);
simon.eatThings();
Try simon.eatThings()
...
Try Person.prototype.eatThings()
...
Try Mammal.eatThings()
...
Try Object.eatThings()
...
Give up
53. In 2004, no one used libraries...
†Well, sort of...
†If you wanted to write anything interesting in JavaScript, there were a
few utility functions you needed in every single project
55. Adding events
var link = document.getElementById(âmylinkâ);
link.onclick = function() {
alert(this.href);
return false;
}
56. Adding more than one event?
// W3C standard browsers
var link = document.getElementById('mylink');
link.addEventListener('click', function() {
alert("Hello");
return false;
});
// IE 6
link.attachEvent("onclick", function() {
alert("Hello");
return false;
);
57. The addEvent function
function addEvent(obj, evType, fn, useCapture){
  if (obj.addEventListener){
    obj.addEventListener(evType, fn, useCapture);
    return true;
  } else if (obj.attachEvent){
    var r = obj.attachEvent("on"+evType, fn);
    return r;
  } else {
    alert("Handler could not be attached");
  }
}
// DON'T USE THIS THOUGH
http://www.scottandrew.com/weblog/articles/cbs-events
58. †What if you want to keep track of the event listeners that have been
added?
†In particular so you can manually de-register them when the page
unloads, to clean up potential memory leaks in IE
†addEvent doesn't ïŹx the different event objects for you, so you still
have to work around browser differences there
addEvent drawbacks
59. Dean Edwards addEvent
function addEvent(element, type, handler) {
" if (element.addEventListener) {
" " element.addEventListener(type, handler, false);
" } else {
" " // assign each event handler a unique ID
" " if (!handler.$$guid) handler.$$guid = addEvent.guid++;
" " // create a hash table of event types for the element
" " if (!element.events) element.events = {};
" " // create a hash table of event handlers for each element/event pair
" " var handlers = element.events[type];
" " if (!handlers) {
" " " handlers = element.events[type] = {};
" " " // store the existing event handler (if there is one)
" " " if (element["on" + type]) {
" " " " handlers[0] = element["on" + type];
" " " }
" " }
" " // store the event handler in the hash table
" " handlers[handler.$$guid] = handler;
" " // assign a global event handler to do all the work
" " element["on" + type] = handleEvent;
" }
};
// a counter used to create unique IDs
addEvent.guid = 1;
60. Dean Edwards addEvent (2)
function removeEvent(element, type, handler) {
" if (element.removeEventListener) {
" " element.removeEventListener(type, handler, false);
" } else {
" " // delete the event handler from the hash table
" " if (element.events && element.events[type]) {
" " " delete element.events[type][handler.$$guid];
" " }
" }
};
function handleEvent(event) {
" var returnValue = true;
" // grab the event object (IE uses a global event object)
" event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
" // get a reference to the hash table of event handlers
" var handlers = this.events[event.type];
" // execute each event handler
" for (var i in handlers) {
" " this.$$handleEvent = handlers[i];
" " if (this.$$handleEvent(event) === false) {
" " " returnValue = false;
" " }
" }
" return returnValue;
};
65. Animate a div moving across a screen
†Easy! Just use setInterval() to move it left 10 pixels every 10th of a
second
†But... what if you're animating lots of things, and the user's
computer can't keep up...
†Solution: ïŹgure out where you want it to be in 2 seconds time, then
check how much time has elapsed each time round the animation
loop and adjust the position accordingly
67. †Watch out for an onmousedown event over the object you want to
drag
†Attach an onmousemove event to the body, and move the element
with the mouse
†Watch for onmouseup, and remove the mousemove handler
†Simple right?
Drag and drop implementation
68. Drag and drop, for real
†Need to be able to distinguish between a click and a drag
†How about... a drag starts when
†The user moves their mouse at least 5 pixels
†OR... the user holds down the mose button on the draggable for at
least a full second
†Need to restrict the area in which the draggable can be dragged
â€
Highlight drop targets when the draggable intersects them
†Revert the position of the item if itâs dropped in the wrong place...
70. The truth is...
†By the time youâve implemented event handling, basic animation,
DOM manipulation and drag and drop, youâve re-invented a sizable
chunk of jQuery, YUI or Dojo, probably with more lines of code and
deïŹnitely with a whole lot more bugs.