rediscovering javascript: the language behind the libraries

80
13th September 2010 Rediscovering JavaScript The Language Behind The Libraries Simon Willison, Think Vitamin JavaScript

Upload: simon-willison

Post on 06-May-2015

7.020 views

Category:

Technology


14 download

TRANSCRIPT

Page 1: Rediscovering JavaScript: The Language Behind The Libraries

13th September 2010

Rediscovering JavaScriptThe Language Behind The LibrariesSimon Willison, Think Vitamin JavaScript

Page 2: Rediscovering JavaScript: The Language Behind The Libraries

Coming up...

✤ JavaScript, the language

✤ Object fundamentals

✤ Functions and closures

✤ Prototype inheritance

✤ JavaScript, the Libraries

✤ Event handling

✤ Animation

✤ Drag ‘n’ Drop

Page 3: Rediscovering JavaScript: The Language Behind The Libraries

Let’s go back in time to 2004...

Page 4: Rediscovering JavaScript: The Language Behind The Libraries

My phone looked like this:Nokia 7610

Page 5: Rediscovering JavaScript: The Language Behind The Libraries

No one took JavaScript seriously

Page 6: Rediscovering JavaScript: The Language Behind The Libraries

Well... almost no one...

Page 7: Rediscovering JavaScript: The Language Behind The Libraries

“Selling the Future of DHTML”

Page 8: Rediscovering JavaScript: The Language Behind The Libraries

Then, 2005 happened

Page 9: Rediscovering JavaScript: The Language Behind The Libraries

May 9th, 2005 Http://www.flickr.com/photos/nataliedowne/14517351/

(me, in 2005)

Page 10: Rediscovering JavaScript: The Language Behind The Libraries

Also in 2005

✤ Gmail had its first 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

Page 11: Rediscovering JavaScript: The Language Behind The Libraries

A flurry of library activity

✤ February 2005: First Prototype.js

✤ March 2005: First public MochiKit code

✤ YUI started development internally at Yahoo! in 2005 (first public release was February 2006)

✤ jQuery released at BarCamp Boston in January 2006

Page 12: Rediscovering JavaScript: The Language Behind The Libraries

Different philosophical approaches

Page 13: Rediscovering JavaScript: The Language Behind The Libraries

Prototype: “make JS like Ruby”

Sortable.tree(element, arguments[1]).children.map(function(item) {

return [

name + Sortable._constructIndex(item) + "[id]=" +

encodeURIComponent(item.id)

].concat(item.children.map(arguments.callee));

}).flatten().join('&');

Page 14: Rediscovering JavaScript: The Language Behind The Libraries

MochiKit: “make JS like Python”

var theSum = sum(takewhile(

partial(operator.gt, 10),

imap(

partial(operator.mul, 2),

count()

)

));

Page 15: Rediscovering JavaScript: The Language Behind The Libraries

YUI: “make JS like Java”

YAHOO.namespace("example.panel");

function initWait(ev) {

YAHOO.example.panel.wait = new YAHOO.widget.Panel("wait", {

width: "240px",

modal: true,

effect: {

effect:YAHOO.widget.ContainerEffect.FADE, duration:0.5

}

});

YAHOO.example.panel.wait.beforeRenderEvent.subscribe(function() {

debug('beforeRenderEvent Fired..');

}, YAHOO.example.panel.wait, true);

YAHOO.example.panel.wait.setHeader("Loading (1), please wait...");

}

Page 16: Rediscovering JavaScript: The Language Behind The Libraries

jQuery: “make JS like jQuery”

$('form#login')

.find('label.optional').hide().end()

.find('input:password').css('border', '1px solid red').end()

.submit(function(){

return confirm('Are you sure you want to submit?');

});

Page 17: Rediscovering JavaScript: The Language Behind The Libraries

How can one language support so many different programming styles?

Page 18: Rediscovering JavaScript: The Language Behind The Libraries

JavaScript, the Language

Page 19: Rediscovering JavaScript: The Language Behind The Libraries

Objects

Page 20: Rediscovering JavaScript: The Language Behind The Libraries

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

Page 21: Rediscovering JavaScript: The Language Behind The Libraries

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

};

Page 22: Rediscovering JavaScript: The Language Behind The Libraries

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

Page 23: Rediscovering JavaScript: The Language Behind The Libraries

(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

Page 24: Rediscovering JavaScript: The Language Behind The Libraries

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

Page 25: Rediscovering JavaScript: The Language Behind The Libraries

Iteration through arrays

> var a = ["dog", "cat", "chicken"];

> for (var i = 0; i < a.length; i++) {

console.log(a[i]);

}

dog

cat

chicken

Page 26: Rediscovering JavaScript: The Language Behind The Libraries

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

Page 27: Rediscovering JavaScript: The Language Behind The Libraries

Functions

Page 28: Rediscovering JavaScript: The Language Behind The Libraries

Functions

Page 29: Rediscovering JavaScript: The Language Behind The Libraries

Functions

// What could be simpler?

function addTwoNumbers(a, b) {

var total = a + b; // A local variable

return total;

}

> addTwoNumbers(2, 4)

6

Page 30: Rediscovering JavaScript: The Language Behind The Libraries

Functions

// What could be simpler?

function addTwoNumbers(a, b) {

var total = a + b; // A local variable

return total;

}

> addTwoNumbers(2, 4)

6

> addTwoNumbers()

NaN

Page 31: Rediscovering JavaScript: The Language Behind The Libraries

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

Page 32: Rediscovering JavaScript: The Language Behind The Libraries

Function parameters are morelike 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

Page 33: Rediscovering JavaScript: The Language Behind The Libraries

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;

})();

Page 34: Rediscovering JavaScript: The Language Behind The Libraries

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

Page 35: Rediscovering JavaScript: The Language Behind The Libraries

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

Page 36: Rediscovering JavaScript: The Language Behind The Libraries

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

Page 37: Rediscovering JavaScript: The Language Behind The Libraries

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

Page 38: Rediscovering JavaScript: The Language Behind The Libraries

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"

Page 39: Rediscovering JavaScript: The Language Behind The Libraries

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"

Page 40: Rediscovering JavaScript: The Language Behind The Libraries

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

Page 41: Rediscovering JavaScript: The Language Behind The Libraries

“this” is JavaScript’s magic word

Page 42: Rediscovering JavaScript: The Language Behind The Libraries

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"

Page 43: Rediscovering JavaScript: The Language Behind The Libraries

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)

Page 44: Rediscovering JavaScript: The Language Behind The Libraries

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"

Page 45: Rediscovering JavaScript: The Language Behind The Libraries

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

Page 46: Rediscovering JavaScript: The Language Behind The Libraries

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?

Page 47: Rediscovering JavaScript: The Language Behind The Libraries

Prototype inheritance

function Person(first, last) { this.first = first; this.last = last; }

Person.prototype.fullName = function() {

return this.first + ' ' + this.last;

}

> var simon = new Person("Simon", "Willison");

> simon.fullName();

"Simon Willison"

> simon.fullNameReversed();

TypeError: Object #<a Person> has no method 'fullNameReversed'

Person.prototype.fullNameReversed = function() {

return this.last + ', ' + this.first;

}

> simon.fullNameReversed();

"Willison, Simon"

Page 48: Rediscovering JavaScript: The Language Behind The Libraries

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"

Page 49: Rediscovering JavaScript: The Language Behind The Libraries

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

Page 50: Rediscovering JavaScript: The Language Behind The Libraries

Prototype chain

var simon = new Person(...);

simon.toString();

Try simon.toString()...

Try Person.prototype.toString()...

Try Object.toString()...

Give up

Page 51: Rediscovering JavaScript: The Language Behind The Libraries

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

Page 52: Rediscovering JavaScript: The Language Behind The Libraries

Let’s talk about libraries

Page 53: Rediscovering JavaScript: The Language Behind The Libraries

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

Page 54: Rediscovering JavaScript: The Language Behind The Libraries

We’ll start with something easy... Events

Page 55: Rediscovering JavaScript: The Language Behind The Libraries

Adding events

var link = document.getElementById(‘mylink’);

link.onclick = function() {

alert(this.href);

return false;

}

Page 56: Rediscovering JavaScript: The Language Behind The Libraries

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;

);

Page 57: Rediscovering JavaScript: The Language Behind The Libraries

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

Page 58: Rediscovering JavaScript: The Language Behind The Libraries

✤ 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 fix the different event objects for you, so you still have to work around browser differences there

addEvent drawbacks

Page 59: Rediscovering JavaScript: The Language Behind The Libraries

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;

Page 60: Rediscovering JavaScript: The Language Behind The Libraries

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;

};

Page 61: Rediscovering JavaScript: The Language Behind The Libraries

Dean Edwards addEvent (3)

function fixEvent(event) {

" // add W3C standard event methods

" event.preventDefault = fixEvent.preventDefault;

" event.stopPropagation = fixEvent.stopPropagation;

" return event;

};

fixEvent.preventDefault = function() {

" this.returnValue = false;

};

fixEvent.stopPropagation = function() {

" this.cancelBubble = true;

};

Page 62: Rediscovering JavaScript: The Language Behind The Libraries

Want to know where the mouse is?

addEvent(div, 'mouseover', function(ev) {

" if (!ev) var ev = window.event; // For IE

" var posx = 0;

" var posy = 0;

" if (ev.pageX || ev.pageY) {

" " posx = ev.pageX;

" " posy = ev.pageY;

" } else if (e.clientX || e.clientY) {

" " posx = e.clientX + document.body.scrollLeft

" " " + document.documentElement.scrollLeft;

" " posy = e.clientY + document.body.scrollTop

" " " + document.documentElement.scrollTop;

" }

});

// http://www.quirksmode.org/js/events_properties.html

Page 64: Rediscovering JavaScript: The Language Behind The Libraries

How about animation?

Page 65: Rediscovering JavaScript: The Language Behind The Libraries

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: figure 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

Page 66: Rediscovering JavaScript: The Language Behind The Libraries

Drag and Drop?

Page 67: Rediscovering JavaScript: The Language Behind The Libraries

✤ 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

Page 68: Rediscovering JavaScript: The Language Behind The Libraries

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

Page 70: Rediscovering JavaScript: The Language Behind The Libraries

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 definitely with a whole lot more bugs.

Page 71: Rediscovering JavaScript: The Language Behind The Libraries

So how does jQuery do it?

Page 72: Rediscovering JavaScript: The Language Behind The Libraries

A simple example

jQuery(function($) {

var div = $('#sessions-placeholder');

$('ul.tags a').click(function(ev) {

ev.preventDefault();

var a = $(this);

div.html(

'<img src="/static/img/loaders/ajax-loader-blue.gif" ' +

'style="margin-bottom: 1em" />'

);

div.load(a.attr('href') + '?ajax=1');

a.closest('ul').find('li').removeClass('selected');

a.closest('li').addClass('selected');

});

});

Page 73: Rediscovering JavaScript: The Language Behind The Libraries

A simple example

jQuery(function($) {

var div = $('#sessions-placeholder');

$('ul.tags a').click(function(ev) {

ev.preventDefault();

var a = $(this);

div.html(

'<img src="/static/img/loaders/ajax-loader-blue.gif" ' +

'style="margin-bottom: 1em" />'

);

div.load(a.attr('href') + '?ajax=1');

a.closest('ul').find('li').removeClass('selected');

a.closest('li').addClass('selected');

});

});

Page 74: Rediscovering JavaScript: The Language Behind The Libraries

A simple example

jQuery(function($) {

var div = $('#sessions-placeholder');

$('ul.tags a').click(function(ev) {

ev.preventDefault();

var a = $(this);

div.html(

'<img src="/static/img/loaders/ajax-loader-blue.gif" ' +

'style="margin-bottom: 1em" />'

);

div.load(a.attr('href') + '?ajax=1');

a.closest('ul').find('li').removeClass('selected');

a.closest('li').addClass('selected');

});

});

Page 75: Rediscovering JavaScript: The Language Behind The Libraries

A simple example

jQuery(function($) {

var div = $('#sessions-placeholder');

$('ul.tags a').click(function(ev) {

ev.preventDefault();

var a = $(this);

div.html(

'<img src="/static/img/loaders/ajax-loader-blue.gif" ' +

'style="margin-bottom: 1em" />'

);

div.load(a.attr('href') + '?ajax=1');

a.closest('ul').find('li').removeClass('selected');

a.closest('li').addClass('selected');

});

});

Page 76: Rediscovering JavaScript: The Language Behind The Libraries

A simple example

jQuery(function($) {

var div = $('#sessions-placeholder');

$('ul.tags a').click(function(ev) {

ev.preventDefault();

var a = $(this);

div.html(

'<img src="/static/img/loaders/ajax-loader-blue.gif" ' +

'style="margin-bottom: 1em" />'

);

div.load(a.attr('href') + '?ajax=1');

a.closest('ul').find('li').removeClass('selected');

a.closest('li').addClass('selected');

});

});

Page 77: Rediscovering JavaScript: The Language Behind The Libraries

A simple example

jQuery(function($) {

var div = $('#sessions-placeholder');

$('ul.tags a').click(function(ev) {

ev.preventDefault();

var a = $(this);

div.html(

'<img src="/static/img/loaders/ajax-loader-blue.gif" ' +

'style="margin-bottom: 1em" />'

);

div.load(a.attr('href') + '?ajax=1');

a.closest('ul').find('li').removeClass('selected');

a.closest('li').addClass('selected');

});

});

Page 78: Rediscovering JavaScript: The Language Behind The Libraries

A simple example

jQuery(function($) {

var div = $('#sessions-placeholder');

$('ul.tags a').click(function(ev) {

ev.preventDefault();

var a = $(this);

div.html(

'<img src="/static/img/loaders/ajax-loader-blue.gif" ' +

'style="margin-bottom: 1em" />'

);

div.load(a.attr('href') + '?ajax=1');

a.closest('ul').find('li').removeClass('selected');

a.closest('li').addClass('selected');

});

});

Page 79: Rediscovering JavaScript: The Language Behind The Libraries

Moral:Learn JavaScript properly, but don’t write your own library unless you’re a total glutton for punishment