JavaScript Survival Guide

Post on 23-Sep-2014

20 views 0 download

Tags:

description

How to stop to worry and starting to love JavaScript

transcript

Giordano Scalzo

JavaScript Survival Guide

Roma,5 marzo 2011

I’m not a guru!

I’m still learning

Java/C++ guy in the past

Ruby/ObjectiveC guy in the present

Why to learn JavaScript?

JavaScript isn’t this anymore

JavaScript is everywhere!

JavaScript is popular!

Technology Radar

JavaScript is trendy!

Technology Radar

JavaScript is trendy!

It should be easy...

Hacked by Brendan Eich in one week...

At the beginning...

after a while...

traps everywhere..

traps everywhere..

and...

:-(

and so...

Back to study!

Started a notebook...

Essential

Scope

function sum(x, y){ // implied global result = x + y; return result;}

{antipattern}

Global variables are evil!

Variables clash

Always declare variables with var

function sum(x, y){ var result = x + y; return result;}

{pattern}

function foo(){ var a = b = 0; //...}

{antipattern}

function foo(){ var a = (b = 0); //...}

{antipattern}

b become global

function foo(){ var a, b; a = b = 0; //...}

don’t use assign chain in definition

{pattern}

Hoisting

myname = "global"; // global variable function func(){ // code... console.log(myname); // "undefined"

// code... var myname = "local"; console.log(myname); // "local"}

func();

{antipattern}

Hoisting

myname = "global"; // global variable function func(){ // code... console.log(myname); // "undefined"

// code... var myname = "local"; console.log(myname); // "local"}

func();

{antipattern}

myname = "global"; // global variable function func(){ // code... console.log(myname); // "undefined"

// code... var myname = "local"; console.log(myname); // "local"}

func();

Hoisting

{antipattern}

Hoisting

myname = "global"; // global variable function func(){ var myname = "declared"; // code... console.log(myname); // "declared"

// code... myname = "local"; console.log(myname); // "local"}

func();

{pattern}

“Variables should be declared as close to their usage as possible”

Robert C. Martin - Clean Code

Against minimum vertical distance principle

function func(){ var a = 1,

b = 2, sum = a + b, myobject = {}, i, j;

// function body...}

{pattern}

Single var pattern

Don’t forget comma otherwise...

... variables become globals

EssentialLiteral and Constructor

In JavaScript almost everything is an object

var person = new Object();person.name = "Scott";person.say = function(){ return "I am " + this.name;};

console.log(person.say());

It’s easy...

var person = new Object();person.name = "Scott";person.say = function(){ return "I am " + this.name;};

console.log(person.say());

{antipattern}

but wrong! :-(

var person = {};person.name = "Scott";person.say = function(){ return "I am " + this.name;};

console.log(person.say());

{pattern}

What if we need similar objects...

var person = {};person.name = "Scott";person.say = function(){ return "I am " + this.name;};console.log(person.say()); // I am Scott

var otherPerson = {};otherPerson.name = "Tiger";otherPerson.say = function(){ return "I am " + this.name;};

console.log(otherPerson.say()); // I am Tiger

A lot of duplication

var person = {};person.name = "Scott";person.say = function(){ return "I am " + this.name;};console.log(person.say()); // I am Scott

var otherPerson = {};otherPerson.name = "Tiger";otherPerson.say = function(){ return "I am " + this.name;};

console.log(otherPerson.say()); // I am Tiger

Duplication is evil!

var Person = function(name){ this.name = name; this.say = function(){ return "I am " + this.name; }}var person = new Person("Scott");

console.log(person.say()); // I am Scott

Custom Constructor Functions

{pattern}

Behind the scenes...

var Person = function(name){ // var this = {}; // this.prototype = {constructor: this} this.name = name; this.say = function(){ return "I am " + this.name; }; // return this;};

{pattern}

var Person = function(name){ this.name = name; this.say = function(){ return "I am " + this.name; };};

var scott = new Person('Scott');var tiger = new Person('Tiger');

console.log(scott.say());console.log(tiger.say());

So, at the end...

{pattern}

What if we forget new?

var Person = function(name){ this.name = name; this.say = function(){ return "I am " + this.name; };};

var scott = new Person('Scott')var adam = Person('Adam')

console.log(typeof scott); //objectconsole.log(scott.name); // Scottconsole.log(typeof adam); //'undefined'console.log(window.name); // Adam

this will point to global object

var Person = function(name){ this.name = name; this.say = function(){ return "I am " + this.name; };};

var scott = new Person('Scott')var adam = Person('Adam')

console.log(typeof scott); //objectconsole.log(scott.name); // Scottconsole.log(typeof adam); //'undefined'console.log(window.name); // Adam

this will point to global object

Enforce new pattern one: naming convention

var Person = function(name){ var that = {}; that.name = name; that.say = function(){ return "I am " + that.name;}; return that;};

var scott = new Person('Scott')var adam = Person('Adam')

console.log(typeof scott); //Objectconsole.log(scott.name); // Scott

console.log(typeof adam); //Objectconsole.log(adam.name); // Adam

{pattern}

var Person = function(name){ var that = {}; that.name = name; that.say = function(){ return "I am " + that.name;}; return that;};

var scott = new Person('Scott')var adam = Person('Adam')

console.log(typeof scott); //Objectconsole.log(scott.name); // Scott

console.log(typeof adam); //Objectconsole.log(adam.name); // Adam

{pattern}

Drawback: we loose prototype reference :-(

var Person = function(name){ var that = {}; that.name = name; that.say = function(){ return "I am " + that.name; }; return that;};

Person.prototype.iamhumanbeing = true;

var scott = new Person('Scott')var adam = Person('Adam')

console.log(scott.iamhumanbeing); // undefinedconsole.log(adam.iamhumanbeing); // undefined

Drawback: we loose prototype reference :-(

var Person = function(name){ var that = {}; that.name = name; that.say = function(){ return "I am " + that.name; }; return that;};

Person.prototype.iamhumanbeing = true;

var scott = new Person('Scott')var adam = Person('Adam')

console.log(scott.iamhumanbeing); // undefinedconsole.log(adam.iamhumanbeing); // undefined

Interm!zoPrototype property

Define ancestors chain

var foo = {one: 1, two: 2};var bar = {three: 3};foo.__proto__ = bar;console.log(foo.one);console.log(foo.three);

Define ancestors chain

var foo = {one: 1, two: 2};var bar = {three: 3};foo.__proto__ = bar;console.log(foo.one);console.log(foo.three);

foo

one: 1

__proto__

bar

three: 3

two: 2

console.log(foo.one);

foo

one: 1

__proto__

bar

three: 3

two: 2

console.log(foo.one);

foo

one: 1

__proto__

bar

three: 3

two: 2

console.log(foo.three);

foo

one: 1

__proto__

bar

three: 3

two: 2

console.log(foo.three);

foo

one: 1

__proto__

bar

three: 3

two: 2

console.log(foo.three);

Interm!zoEnd

var Person = function(name){ // this.prototype = {constructor: this} var that = {}; that.name = name; that.say = function(){ return "I am " + that.name; }; return that;};

Behind the scenes...

var Person = function(name){ if (this instanceof Person) { this.name = name; this.say = function(){ return "I am " + that.name; } } else { return new Person(name); }};Person.prototype.iamhumanbeing = true;var scott = new Person('Scott')var adam = Person('Adam')console.log(scott.name); // Scottconsole.log(adam.name); // Adamconsole.log(scott.iamhumanbeing); // trueconsole.log(adam.iamhumanbeing); // true

Self invoking constructor

{pattern}

var Person = function(name){ if (this instanceof Person) { this.name = name; this.say = function(){ return "I am " + that.name; } } else { return new Person(name); }};Person.prototype.iamhumanbeing = true;var scott = new Person('Scott')var adam = Person('Adam')console.log(scott.name); // Scottconsole.log(adam.name); // Adamconsole.log(scott.iamhumanbeing); // trueconsole.log(adam.iamhumanbeing); // true

Self invoking constructor

{pattern}

var Person = function(name){ if (this instanceof Person) { this.name = name; this.say = function(){ return "I am " + that.name; } } else { return new Person(name); }};Person.prototype.iamhumanbeing = true;var scott = new Person('Scott')var adam = Person('Adam')console.log(scott.name); // Scottconsole.log(adam.name); // Adamconsole.log(scott.iamhumanbeing); // trueconsole.log(adam.iamhumanbeing); // true

Self invoking constructor

{pattern}

var Person = function(name){ if (this instanceof Person) { this.name = name; this.say = function(){ return "I am " + that.name; } } else { return new Person(name); }};Person.prototype.iamhumanbeing = true;var scott = new Person('Scott')var adam = Person('Adam')console.log(scott.name); // Scottconsole.log(adam.name); // Adamconsole.log(scott.iamhumanbeing); // trueconsole.log(adam.iamhumanbeing); // true

Self invoking constructor

{pattern}

EssentialFunctions

Functions as first class objects

Immediate functions

(function(){ alert('watch out!');})();

Initialization pattern

(function(){ var days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], today = new Date(), msg = 'Today is ' + days[today.getDay()] + ', ' + today.getDate(); console.log(msg);})(); // "Today is Wed, 10"

{pattern}

Function scope

// constructors function Parent(){}

function Child(){}

// a variablevar some_var = 1;// some objects var module1 = {};module1.data = { a: 1, b: 2};var module2 = {};

{antipattern}

5 globals...

// global object var MYAPP = (function(){ var my = {}; // constructors my.Parent = function(){}; my.Child = function(){}; // a variable my.some_var = 1; // an object container my.modules = {}; // nested objects my.modules.module1 = {}; my.modules.module1.data = { a: 1, b: 2 }; my.modules.module2 = {}; return my;})();

console.log(MYAPP.modules.module1.data.a); // 1

{pattern}1 global!

// global object var MYAPP = (function(){ var my = {}; // constructors my.Parent = function(){}; my.Child = function(){}; // a variable my.some_var = 1; // an object container my.modules = {}; // nested objects my.modules.module1 = {}; my.modules.module1.data = { a: 1, b: 2 }; my.modules.module2 = {}; return my;})();

console.log(MYAPP.modules.module1.data.a); // 1

{pattern}1 global!

What about encapsulation?

function Gadget(){ this.name = 'iPod'; this.stretch = function(){ return 'iPad'; }};

var toy = new Gadget();console.log(toy.name); // `iPod` toy.name = 'Zune'console.log(toy.name); // `Zune` is public console.log(toy.stretch()); // stretch() is public

{antipattern}

function Gadget(){ var name = 'iPod'; this.getName = function(){ return name; }};

var toy = new Gadget();console.log(toy.getName()); // `iPod`toy.name = 'Zune'console.log(toy.getName()); // `iPod`

Create private member

{pattern}

function Gadget(){ var name = 'iPod'; this.getName = function(){ return name; }};

var toy = new Gadget();console.log(toy.getName()); // `iPod`toy.name = 'Zune'console.log(toy.getName()); // `iPod`

Create private member

{pattern}

function Gadget(){ var name = 'iPod'; this.getName = function(){ return name; }};

var toy = new Gadget();console.log(toy.getName()); // `iPod`toy.name = 'Zune'console.log(toy.getName()); // `iPod`

Create private member

{pattern}

function Gadget(){ var name = 'iPod'; this.getName = function(){ return name; }};

var toy = new Gadget();console.log(toy.getName()); // `iPod`toy.name = 'Zune'console.log(toy.getName()); // `iPod`

Create private member

{pattern}

function Gadget() { var name = 'iPod'; var upgrade = function(){ return 'iPhone'; }

this.getName = function () { return name; } this.pay = function() { return upgrade(); }};

var toy = new Gadget(); console.log(toy.pay()); // `iPhone`console.log(toy.upgrade()); // `error`

{pattern}

for methods too

AdvancedTest Framework

JavaScript is madly in love with Testing.

JavaScript is madly in love with Testing.

JSUnit

JavaScript is madly in love with Testing.

JSUnit

JSTest

JavaScript is madly in love with Testing.

QUnit JSUnit

JSTest

JavaScript is madly in love with Testing.

QUnit JSUnit

JSTest Vows

JavaScript is madly in love with Testing.

QUnit JSUnit

JSTestjsUnitTest Vows

JavaScript is madly in love with Testing.

Blue Ridge

QUnit JSUnit

JSTestjsUnitTest Vows

Screw unit

JavaScript is madly in love with Testing.

Blue Ridge

QUnit JSUnit

JSTestjsUnitTest Vows

Screw unit

JavaScript is madly in love with Testing.

Blue Ridge

QUnit JSUnit

JSTest

YUI Test

jsUnitTest Vows

Screw unit

JavaScript is madly in love with Testing.

Blue Ridge

JSpecQUnit JSUnit

JSTest

YUI Test

jsUnitTest Vows

Screw unit

JavaScript is madly in love with Testing.

Blue Ridge

JSpecQUnit JSUnit

JSTest

UnitestingYUI Test

jsUnitTest Vows

Screw unit

JavaScript is madly in love with Testing.

Blue Ridge

JSpecQUnit JSUnit

JSTest

UnitestingYUI Test

jsUnitTest Vows

Jasmine

JavaScript is madly in love with Testing.

http://pivotal.github.com/jasmine/

describe('Array', function() { var array;

describe('#unique', function() { beforeEach(function() { array = [3,3,4]; });

it("returns only the unique values in the array", function() { expect(array.unique()).toEqual([3,4]); }); });});

Jasmine: BDD RSpec-like syntax

Not mocks and Stubs

Spies

function BetterArray(array){ this.array = array;}

BetterArray.prototype = { joinify: function(){ // Does some cool stuff this.array = this.array.join('.'); }}

Jasmine: Spies as Mocks

describe('BetterArray', function() { var betterArray, anArray = [3,3,4];

beforeEach(function() { betterArray = new BetterArray(anArray); });

describe('#joinify', function() { it("calls the standard join method on Array", function() { var joinSpy = spyOn(betterArray.array, 'join'); betterArray.joinify(); expect(joinSpy).toHaveBeenCalled(); }); });});

Jasmine: Spies as Mocks

describe('BetterArray', function() { var betterArray, anArray = [3,3,4];

beforeEach(function() { betterArray = new BetterArray(anArray); });

describe('#joinify', function() { it("calls the standard join method on Array", function() { var joinSpy = spyOn(betterArray.array, 'join'); betterArray.joinify(); expect(joinSpy).toHaveBeenCalled(); }); });});

Jasmine: Spies as Mocks

describe('BetterArray', function() { var betterArray, anArray = [3,3,4];

beforeEach(function() { betterArray = new BetterArray(anArray); });

describe('#joinify', function() { it("calls the standard join method on Array", function() { var joinSpy = spyOn(betterArray.array, 'join'); betterArray.joinify(); expect(joinSpy).toHaveBeenCalled(); }); });});

Jasmine: Spies as Mocks

describe('BetterArray', function() { var betterArray, anArray = [3,3,4];

beforeEach(function() { betterArray = new BetterArray(anArray); });

describe('#joinify', function() { it("calls the standard join method on Array", function() { var joinSpy = spyOn(betterArray.array, 'join'); betterArray.joinify(); expect(joinSpy).toHaveBeenCalled(); }); });});

Jasmine: Spies as Mocks

AdvancedBrowser Patterns

JavaScript still live into browser...

for (var i = 0; i < 100; i += 1) { document.getElementById("result").innerHTML += i + ", ";}

Dom access: write

{antipattern}

var i, content = "";for (i = 0; i < 100; i += 1) { content += i + ",";}document.getElementById("result").innerHTML += content;

Dom access: update local variable

{pattern}

var padding = document.getElementById("result").style.padding, margin = document.getElementById("result").style.margin;

Dom access: read

{antipattern}

var style = document.getElementById("result").style, padding = style.padding, margin = style.margin;

{pattern}

Dom access: read with local variable

Dom manipulation

{antipattern}

// appending nodes as they are created var p, t;p = document.createElement('p'); t = document.createTextNode('first paragraph'); p.appendChild(t); document.body.appendChild(p);

p = document.createElement('p'); t = document.createTextNode('second paragraph'); p.appendChild(t); document.body.appendChild(p);

Dom manipulation

{antipattern}

// appending nodes as they are created var p, t;p = document.createElement('p'); t = document.createTextNode('first paragraph'); p.appendChild(t); document.body.appendChild(p);

p = document.createElement('p'); t = document.createTextNode('second paragraph'); p.appendChild(t); document.body.appendChild(p);

var p, t, frag;frag = document.createDocumentFragment();p = document.createElement('p'); t = document.createTextNode('first paragraph'); p.appendChild(t); frag.appendChild(p);

p = document.createElement('p'); t = document.createTextNode('second paragraph'); p.appendChild(t); frag.appendChild(p);document.body.appendChild(frag);

Dom manipulation

{pattern}

Dom manipulation

{pattern}

var p, t, frag;frag = document.createDocumentFragment();p = document.createElement('p'); t = document.createTextNode('first paragraph'); p.appendChild(t); frag.appendChild(p);

p = document.createElement('p'); t = document.createTextNode('second paragraph'); p.appendChild(t); frag.appendChild(p);document.body.appendChild(frag);

Dom manipulation

{pattern}

var p, t, frag;frag = document.createDocumentFragment();p = document.createElement('p'); t = document.createTextNode('first paragraph'); p.appendChild(t); frag.appendChild(p);

p = document.createElement('p'); t = document.createTextNode('second paragraph'); p.appendChild(t); frag.appendChild(p);document.body.appendChild(frag);

Dom manipulation

{pattern}

var p, t, frag;frag = document.createDocumentFragment();p = document.createElement('p'); t = document.createTextNode('first paragraph'); p.appendChild(t); frag.appendChild(p);

p = document.createElement('p'); t = document.createTextNode('second paragraph'); p.appendChild(t); frag.appendChild(p);document.body.appendChild(frag);

The place of <script> element<!doctype html> <html> <head> <title>My App</title> <script src="jquery.js"></script> <script src="jquery.quickselect.js"></script> <script src="jquery.lightbox.js"></script> <script src="myapp.js"></script> </head> <body> ... </body></html>

{antipattern}Worst

<!doctype html> <html> <head> <title>My App</title> <script src="all_20110127.js"></script> </head> <body> ... </body></html>

The place of <script> element

{antipattern}

The place of <script> element

{pattern}

<!doctype html> <html> <head> <title>My App</title> </head> <body> ... <script src="all_20110127.js"></script> </body></html>

It’s just a beginning...

Study

“Save it for a rainy day!”

Check your code with jslint.com

http://jashkenas.github.com/coffee-script/

It’s just JavaScript!

# Assignment:number = 42opposite = true

# Conditions:number = -42 if opposite

# Functions:square = (x) -> x * x

# Objects:math = root: Math.sqrt square: square cube: (x) -> x * square x

# Splats:race = (winner, runners...) -> print winner, runners

var cubes, math, num, number, opposite, race, square;var __slice = Array.prototype.slice;number = 42;opposite = true;if (opposite) { number = -42;}square = function(x) { return x * x;};

math = { root: Math.sqrt, square: square, cube: function(x) { return x * square(x); }};race = function() { var runners, winner; winner = arguments[0], runners = 2 <= arguments.length ? __slice.call(arguments, 1) : []; return print(winner, runners);};}

JavaScript

# Assignment:number = 42opposite = true

# Conditions:number = -42 if opposite

# Functions:square = (x) -> x * x

# Objects:math = root: Math.sqrt square: square cube: (x) -> x * square x

# Splats:race = (winner, runners...) -> print winner, runners

var cubes, math, num, number, opposite, race, square;var __slice = Array.prototype.slice;number = 42;opposite = true;if (opposite) { number = -42;}square = function(x) { return x * x;};

math = { root: Math.sqrt, square: square, cube: function(x) { return x * square(x); }};race = function() { var runners, winner; winner = arguments[0], runners = 2 <= arguments.length ? __slice.call(arguments, 1) : []; return print(winner, runners);};}

JavaScript

# Assignment:number = 42opposite = true

# Conditions:number = -42 if opposite

# Functions:square = (x) -> x * x

# Objects:math = root: Math.sqrt square: square cube: (x) -> x * square x

# Splats:race = (winner, runners...) -> print winner, runners

var cubes, math, num, number, opposite, race, square;var __slice = Array.prototype.slice;number = 42;opposite = true;if (opposite) { number = -42;}square = function(x) { return x * x;};

math = { root: Math.sqrt, square: square, cube: function(x) { return x * square(x); }};race = function() { var runners, winner; winner = arguments[0], runners = 2 <= arguments.length ? __slice.call(arguments, 1) : []; return print(winner, runners);};}

JavaScript

# Assignment:number = 42opposite = true

# Conditions:number = -42 if opposite

# Functions:square = (x) -> x * x

# Objects:math = root: Math.sqrt square: square cube: (x) -> x * square x

# Splats:race = (winner, runners...) -> print winner, runners

var cubes, math, num, number, opposite, race, square;var __slice = Array.prototype.slice;number = 42;opposite = true;if (opposite) { number = -42;}square = function(x) { return x * x;};

math = { root: Math.sqrt, square: square, cube: function(x) { return x * square(x); }};race = function() { var runners, winner; winner = arguments[0], runners = 2 <= arguments.length ? __slice.call(arguments, 1) : []; return print(winner, runners);};}

JavaScript

# Assignment:number = 42opposite = true

# Conditions:number = -42 if opposite

# Functions:square = (x) -> x * x

# Objects:math = root: Math.sqrt square: square cube: (x) -> x * square x

# Splats:race = (winner, runners...) -> print winner, runners

var cubes, math, num, number, opposite, race, square;var __slice = Array.prototype.slice;number = 42;opposite = true;if (opposite) { number = -42;}square = function(x) { return x * x;};

math = { root: Math.sqrt, square: square, cube: function(x) { return x * square(x); }};race = function() { var runners, winner; winner = arguments[0], runners = 2 <= arguments.length ? __slice.call(arguments, 1) : []; return print(winner, runners);};}

JavaScript

# Assignment:number = 42opposite = true

# Conditions:number = -42 if opposite

# Functions:square = (x) -> x * x

# Objects:math = root: Math.sqrt square: square cube: (x) -> x * square x

# Splats:race = (winner, runners...) -> print winner, runners

var cubes, math, num, number, opposite, race, square;var __slice = Array.prototype.slice;number = 42;opposite = true;if (opposite) { number = -42;}square = function(x) { return x * x;};

math = { root: Math.sqrt, square: square, cube: function(x) { return x * square(x); }};race = function() { var runners, winner; winner = arguments[0], runners = 2 <= arguments.length ? __slice.call(arguments, 1) : []; return print(winner, runners);};}

JavaScript

# Existence:alert "I knew it!" if elvis?

# Array comprehensions:cubes = (math.cube num for num in list)

if (typeof elvis != "undefined" && elvis !== null) { alert("I knew it!");}

cubes = (function() { var _i, _len, _results; _results = []; for (_i = 0, _len = list.length; _i < _len; _i++) { num = list[_i]; _results.push(math.cube(num)); } return _results;})();

JavaScript

# Existence:alert "I knew it!" if elvis?

# Array comprehensions:cubes = (math.cube num for num in list)

if (typeof elvis != "undefined" && elvis !== null) { alert("I knew it!");}

cubes = (function() { var _i, _len, _results; _results = []; for (_i = 0, _len = list.length; _i < _len; _i++) { num = list[_i]; _results.push(math.cube(num)); } return _results;})();

JavaScript

...but it’s just another story...

@giordanoscalzo

www.slideshare.net/giordano

github.com/gscalzo

giordano.scalzo@cleancode.it

Roma,5 marzo 2011

http://creativecommons.org/licenses/by-nc-sa/3.0/