Post on 23-Sep-2014
description
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/
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/