JavaScript Survival Guide Roma, Giordano Scalzo 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!
JavaScript is trendy! Technology Radar
JavaScript is trendy! Technology Radar
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}
b become global function foo(){ var a = (b = 0); //... } {antipattern}
don’t use assign chain in definition function foo(){ var a, b; a = b = 0; //... } {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}
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(){ var myname = "declared"; // code... console.log(myname); // "declared" // code... myname = "local"; console.log(myname); // "local" } func(); {pattern}
Against minimum vertical distance principle “Variables should be declared as close to their usage as possible” Robert C. Martin - Clean Code
Single var pattern function func(){ var a = 1, b = 2, sum = a + b, myobject = {}, i, j; // function body... } {pattern}
Don’t forget comma otherwise...
... variables become globals
Essential Literal and Constructor
In JavaScript almost everything is an object
It’s easy... var person = new Object(); person.name = "Scott"; person.say = function(){ return "I am " + this.name; }; console.log(person.say());
but wrong! :-( var person = new Object(); person.name = "Scott"; person.say = function(){ return "I am " + this.name; }; console.log(person.say()); {antipattern}
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!
Custom Constructor Functions 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 {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}
So, at the end... 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()); {pattern}
What if we forget new?
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); //object console.log(scott.name); // Scott console.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); //object console.log(scott.name); // Scott console.log(typeof adam); //'undefined' console.log(window.name); // Adam
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); //Object console.log(scott.name); // Scott console.log(typeof adam); //Object console.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); //Object console.log(scott.name); // Scott console.log(typeof adam); //Object console.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); // undefined console.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); // undefined console.log(adam.iamhumanbeing); // undefined
Interm!zo Prototype 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);
console.log(foo.one); bar three: 3 foo one: 1 two: 2 __proto__
console.log(foo.one); bar three: 3 foo one: 1 two: 2 __proto__
console.log(foo.three); bar three: 3 foo one: 1 two: 2 __proto__
console.log(foo.three); bar three: 3 foo one: 1 two: 2 __proto__
console.log(foo.three); bar three: 3 foo one: 1 two: 2 __proto__
End Interm!zo
Behind the scenes... var Person = function(name){ // this.prototype = {constructor: this} var that = {}; that.name = name; that.say = function(){ return "I am " + that.name; }; return that; };
Self invoking constructor 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); // Scott console.log(adam.name); // Adam console.log(scott.iamhumanbeing); // true console.log(adam.iamhumanbeing); // true {pattern}
Self invoking constructor 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); // Scott console.log(adam.name); // Adam console.log(scott.iamhumanbeing); // true console.log(adam.iamhumanbeing); // true {pattern}
Self invoking constructor 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); // Scott console.log(adam.name); // Adam console.log(scott.iamhumanbeing); // true console.log(adam.iamhumanbeing); // true {pattern}
Self invoking constructor 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); // Scott console.log(adam.name); // Adam console.log(scott.iamhumanbeing); // true console.log(adam.iamhumanbeing); // true {pattern}
Essential Functions
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
5 globals... // constructors function Parent(){ } function Child(){ } // a variable var some_var = 1; // some objects var module1 = {}; module1.data = { a: 1, b: 2 }; var module2 = {}; {antipattern}
1 global! // global object var MYAPP = (function(){ {pattern} 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
1 global! // global object var MYAPP = (function(){ {pattern} 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
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}
Create private member 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` {pattern}
Create private member 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` {pattern}
Create private member 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` {pattern}
Create private member 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` {pattern}
for methods too 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}
Advanced Test 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 Vows JSTest
JavaScript is madly in love with Testing. QUnit JSUnit jsUnitTest Vows JSTest
JavaScript is madly in love with Testing. Blue Ridge QUnit JSUnit jsUnitTest Vows JSTest
JavaScript is madly in love with Testing. Screw unit Blue Ridge QUnit JSUnit jsUnitTest Vows JSTest
JavaScript is madly in love with Testing. Screw unit Blue Ridge QUnit JSUnit YUI Test jsUnitTest Vows JSTest
JavaScript is madly in love with Testing. Screw unit Blue Ridge JSpec QUnit JSUnit YUI Test jsUnitTest Vows JSTest
JavaScript is madly in love with Testing. Screw unit Blue Ridge JSpec QUnit JSUnit Unitesting YUI Test jsUnitTest Vows JSTest
JavaScript is madly in love with Testing. Screw unit Blue Ridge JSpec QUnit JSUnit Unitesting Jasmine YUI Test jsUnitTest Vows JSTest
JavaScript is madly in love with Testing. http://pivotal.github.com/jasmine/
Jasmine: BDD RSpec-like syntax 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]); }); }); });
Not mocks and Stubs Spies
Jasmine: Spies as Mocks 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(); }); }); });
Advanced Browser Patterns
JavaScript still live into browser...
Dom access: write for (var i = 0; i < 100; i += 1) { document.getElementById("result").innerHTML += i + ", "; } {antipattern}
Dom access: update local variable var i, content = ""; for (i = 0; i < 100; i += 1) { content += i + ","; } document.getElementById("result").innerHTML += content; {pattern}
Dom access: read var padding = document.getElementById("result").style.padding, margin = document.getElementById("result").style.margin; {antipattern}
Dom access: read with local variable var style = document.getElementById("result").style, padding = style.padding, margin = style.margin; {pattern}
Dom manipulation // 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); {antipattern}
Dom manipulation // 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); {antipattern}
Dom manipulation 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); {pattern}
Dom manipulation 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); {pattern}
Dom manipulation 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); {pattern}
Dom manipulation 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); {pattern}
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> Worst {antipattern}
The place of <script> element <!doctype html> <html> <head> <title>My App</title> <script src="all_20110127.js"></script> </head> <body> ... </body> </html> {antipattern}
The place of <script> element <!doctype html> <html> <head> <title>My App</title> </head> <body> ... <script src="all_20110127.js"></script> </body> </html> {pattern}
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: var cubes, math, num, number, opposite, number = 42 race, square; opposite = true var __slice = Array.prototype.slice; number = 42; opposite = true; # Conditions: if (opposite) { number = -42 if opposite number = -42; } # Functions: square = function(x) { square = (x) -> x * x return x * x; }; # Objects: math = math = { root: Math.sqrt root: Math.sqrt, square: square square: square, cube: (x) -> x * square x cube: function(x) { return x * square(x); } }; race = function() { # Splats: var runners, winner; race = (winner, runners...) -> winner = arguments[0], runners = 2 print winner, runners <= arguments.length ? __slice.call(arguments, 1) : []; return print(winner, runners); };} JavaScript
# Assignment: var cubes, math, num, number, opposite, number = 42 race, square; opposite = true var __slice = Array.prototype.slice; number = 42; opposite = true; # Conditions: if (opposite) { number = -42 if opposite number = -42; } # Functions: square = function(x) { square = (x) -> x * x return x * x; }; # Objects: math = math = { root: Math.sqrt root: Math.sqrt, square: square square: square, cube: (x) -> x * square x cube: function(x) { return x * square(x); } }; race = function() { # Splats: var runners, winner; race = (winner, runners...) -> winner = arguments[0], runners = 2 print winner, runners <= arguments.length ? __slice.call(arguments, 1) : []; return print(winner, runners); };} JavaScript
# Assignment: var cubes, math, num, number, opposite, number = 42 race, square; opposite = true var __slice = Array.prototype.slice; number = 42; opposite = true; # Conditions: if (opposite) { number = -42 if opposite number = -42; } # Functions: square = function(x) { square = (x) -> x * x return x * x; }; # Objects: math = math = { root: Math.sqrt root: Math.sqrt, square: square square: square, cube: (x) -> x * square x cube: function(x) { return x * square(x); } }; race = function() { # Splats: var runners, winner; race = (winner, runners...) -> winner = arguments[0], runners = 2 print winner, runners <= arguments.length ? __slice.call(arguments, 1) : []; return print(winner, runners); };} JavaScript
# Assignment: var cubes, math, num, number, opposite, number = 42 race, square; opposite = true var __slice = Array.prototype.slice; number = 42; opposite = true; # Conditions: if (opposite) { number = -42 if opposite number = -42; } # Functions: square = function(x) { square = (x) -> x * x return x * x; }; # Objects: math = math = { root: Math.sqrt root: Math.sqrt, square: square square: square, cube: (x) -> x * square x cube: function(x) { return x * square(x); } }; race = function() { # Splats: var runners, winner; race = (winner, runners...) -> winner = arguments[0], runners = 2 print winner, runners <= arguments.length ? __slice.call(arguments, 1) : []; return print(winner, runners); };} JavaScript
# Assignment: var cubes, math, num, number, opposite, number = 42 race, square; opposite = true var __slice = Array.prototype.slice; number = 42; opposite = true; # Conditions: if (opposite) { number = -42 if opposite number = -42; } # Functions: square = function(x) { square = (x) -> x * x return x * x; }; # Objects: math = math = { root: Math.sqrt root: Math.sqrt, square: square square: square, cube: (x) -> x * square x cube: function(x) { return x * square(x); } }; race = function() { # Splats: var runners, winner; race = (winner, runners...) -> winner = arguments[0], runners = 2 print winner, runners <= arguments.length ? __slice.call(arguments, 1) : []; return print(winner, runners); };} JavaScript
# Assignment: var cubes, math, num, number, opposite, number = 42 race, square; opposite = true var __slice = Array.prototype.slice; number = 42; opposite = true; # Conditions: if (opposite) { number = -42 if opposite number = -42; } # Functions: square = function(x) { square = (x) -> x * x return x * x; }; # Objects: math = math = { root: Math.sqrt root: Math.sqrt, square: square square: square, cube: (x) -> x * square x cube: function(x) { return x * square(x); } }; race = function() { # Splats: var runners, winner; race = (winner, runners...) -> winner = arguments[0], runners = 2 print winner, runners <= arguments.length ? __slice.call(arguments, 1) : []; return print(winner, runners); };} JavaScript
# Existence: if (typeof elvis != "undefined" && elvis !== null) { alert "I knew it!" if elvis? alert("I knew it!"); } # Array comprehensions: cubes = (function() { cubes = (math.cube num for num in list) 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: if (typeof elvis != "undefined" && elvis !== null) { alert "I knew it!" if elvis? alert("I knew it!"); } # Array comprehensions: cubes = (function() { cubes = (math.cube num for num in list) 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...
giordano.scalzo@cleancode.it @giordanoscalzo www.slideshare.net/giordano github.com/gscalzo Roma,5 marzo 2011 http://creativecommons.org/licenses/by-nc-sa/3.0/

JavaScript Survival Guide

  • 1.
    JavaScript Survival Guide Roma, Giordano Scalzo 5 marzo 2011
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
    Why to learnJavaScript?
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
    It should beeasy...
  • 13.
    Hacked by BrendanEich in one week...
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 22.
  • 23.
  • 24.
  • 25.
    function sum(x, y){ // implied global result = x + y; return result; } {antipattern}
  • 26.
  • 27.
  • 28.
    Always declare variableswith var function sum(x, y){ var result = x + y; return result; } {pattern}
  • 29.
    function foo(){ var a = b = 0; //... } {antipattern}
  • 30.
    b become global function foo(){ var a = (b = 0); //... } {antipattern}
  • 31.
    don’t use assignchain in definition function foo(){ var a, b; a = b = 0; //... } {pattern}
  • 32.
    Hoisting myname = "global"; // global variable function func(){ // code... console.log(myname); // "undefined" // code... var myname = "local"; console.log(myname); // "local" } func(); {antipattern}
  • 33.
    Hoisting myname = "global"; // global variable function func(){ // code... console.log(myname); // "undefined" // code... var myname = "local"; console.log(myname); // "local" } func(); {antipattern}
  • 34.
    Hoisting myname = "global"; // global variable function func(){ // code... console.log(myname); // "undefined" // code... var myname = "local"; console.log(myname); // "local" } func(); {antipattern}
  • 35.
    Hoisting myname = "global"; // global variable function func(){ var myname = "declared"; // code... console.log(myname); // "declared" // code... myname = "local"; console.log(myname); // "local" } func(); {pattern}
  • 36.
    Against minimum vertical distance principle “Variables should be declared as close to their usage as possible” Robert C. Martin - Clean Code
  • 37.
    Single var pattern function func(){ var a = 1, b = 2, sum = a + b, myobject = {}, i, j; // function body... } {pattern}
  • 38.
  • 39.
  • 40.
  • 41.
    In JavaScript almosteverything is an object
  • 42.
    It’s easy... var person= new Object(); person.name = "Scott"; person.say = function(){ return "I am " + this.name; }; console.log(person.say());
  • 43.
    but wrong! :-( var person = new Object(); person.name = "Scott"; person.say = function(){ return "I am " + this.name; }; console.log(person.say()); {antipattern}
  • 44.
    var person ={}; person.name = "Scott"; person.say = function(){ return "I am " + this.name; }; console.log(person.say()); {pattern}
  • 45.
    What if weneed 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
  • 46.
    A lot ofduplication 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
  • 47.
  • 48.
    Custom Constructor Functions 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 {pattern}
  • 49.
    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}
  • 50.
    So, at theend... 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()); {pattern}
  • 51.
    What if weforget new?
  • 52.
    this will pointto 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); //object console.log(scott.name); // Scott console.log(typeof adam); //'undefined' console.log(window.name); // Adam
  • 53.
    this will pointto 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); //object console.log(scott.name); // Scott console.log(typeof adam); //'undefined' console.log(window.name); // Adam
  • 54.
    Enforce new patternone: naming convention
  • 55.
    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); //Object console.log(scott.name); // Scott console.log(typeof adam); //Object console.log(adam.name); // Adam {pattern}
  • 56.
    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); //Object console.log(scott.name); // Scott console.log(typeof adam); //Object console.log(adam.name); // Adam {pattern}
  • 57.
    Drawback: we looseprototype 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); // undefined console.log(adam.iamhumanbeing); // undefined
  • 58.
    Drawback: we looseprototype 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); // undefined console.log(adam.iamhumanbeing); // undefined
  • 59.
  • 60.
    Define ancestors chain var foo = {one: 1, two: 2}; var bar = {three: 3}; foo.__proto__ = bar; console.log(foo.one); console.log(foo.three);
  • 61.
    Define ancestors chain var foo = {one: 1, two: 2}; var bar = {three: 3}; foo.__proto__ = bar; console.log(foo.one); console.log(foo.three);
  • 62.
    console.log(foo.one); bar three: 3 foo one: 1 two: 2 __proto__
  • 63.
    console.log(foo.one); bar three: 3 foo one: 1 two: 2 __proto__
  • 64.
    console.log(foo.three); bar three: 3 foo one: 1 two: 2 __proto__
  • 65.
    console.log(foo.three); bar three: 3 foo one: 1 two: 2 __proto__
  • 66.
    console.log(foo.three); bar three: 3 foo one: 1 two: 2 __proto__
  • 67.
  • 68.
    Behind the scenes... varPerson = function(name){ // this.prototype = {constructor: this} var that = {}; that.name = name; that.say = function(){ return "I am " + that.name; }; return that; };
  • 69.
    Self invoking constructor 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); // Scott console.log(adam.name); // Adam console.log(scott.iamhumanbeing); // true console.log(adam.iamhumanbeing); // true {pattern}
  • 70.
    Self invoking constructor 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); // Scott console.log(adam.name); // Adam console.log(scott.iamhumanbeing); // true console.log(adam.iamhumanbeing); // true {pattern}
  • 71.
    Self invoking constructor 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); // Scott console.log(adam.name); // Adam console.log(scott.iamhumanbeing); // true console.log(adam.iamhumanbeing); // true {pattern}
  • 72.
    Self invoking constructor 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); // Scott console.log(adam.name); // Adam console.log(scott.iamhumanbeing); // true console.log(adam.iamhumanbeing); // true {pattern}
  • 73.
    Essential Functions
  • 74.
    Functions as firstclass objects
  • 75.
    Immediate functions (function(){ alert('watch out!'); })();
  • 76.
    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}
  • 77.
  • 78.
    5 globals... // constructors function Parent(){ } function Child(){ } // a variable var some_var = 1; // some objects var module1 = {}; module1.data = { a: 1, b: 2 }; var module2 = {}; {antipattern}
  • 79.
    1 global! // globalobject var MYAPP = (function(){ {pattern} 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
  • 80.
    1 global! // globalobject var MYAPP = (function(){ {pattern} 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
  • 81.
  • 82.
    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}
  • 83.
    Create private member 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` {pattern}
  • 84.
    Create private member 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` {pattern}
  • 85.
    Create private member 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` {pattern}
  • 86.
    Create private member 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` {pattern}
  • 87.
    for methods too 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}
  • 88.
    Advanced TestFramework
  • 89.
    JavaScript is madlyin love with Testing.
  • 90.
    JavaScript is madlyin love with Testing. JSUnit
  • 91.
    JavaScript is madlyin love with Testing. JSUnit JSTest
  • 92.
    JavaScript is madlyin love with Testing. QUnit JSUnit JSTest
  • 93.
    JavaScript is madlyin love with Testing. QUnit JSUnit Vows JSTest
  • 94.
    JavaScript is madlyin love with Testing. QUnit JSUnit jsUnitTest Vows JSTest
  • 95.
    JavaScript is madlyin love with Testing. Blue Ridge QUnit JSUnit jsUnitTest Vows JSTest
  • 96.
    JavaScript is madlyin love with Testing. Screw unit Blue Ridge QUnit JSUnit jsUnitTest Vows JSTest
  • 97.
    JavaScript is madlyin love with Testing. Screw unit Blue Ridge QUnit JSUnit YUI Test jsUnitTest Vows JSTest
  • 98.
    JavaScript is madlyin love with Testing. Screw unit Blue Ridge JSpec QUnit JSUnit YUI Test jsUnitTest Vows JSTest
  • 99.
    JavaScript is madlyin love with Testing. Screw unit Blue Ridge JSpec QUnit JSUnit Unitesting YUI Test jsUnitTest Vows JSTest
  • 100.
    JavaScript is madlyin love with Testing. Screw unit Blue Ridge JSpec QUnit JSUnit Unitesting Jasmine YUI Test jsUnitTest Vows JSTest
  • 101.
    JavaScript is madlyin love with Testing. http://pivotal.github.com/jasmine/
  • 102.
    Jasmine: BDD RSpec-likesyntax 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]); }); }); });
  • 103.
    Not mocks andStubs Spies
  • 104.
    Jasmine: Spies asMocks function BetterArray(array){ this.array = array; } BetterArray.prototype = { joinify: function(){ // Does some cool stuff this.array = this.array.join('.'); } }
  • 105.
    Jasmine: Spies asMocks 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(); }); }); });
  • 106.
    Jasmine: Spies asMocks 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(); }); }); });
  • 107.
    Jasmine: Spies asMocks 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(); }); }); });
  • 108.
    Jasmine: Spies asMocks 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(); }); }); });
  • 109.
  • 110.
    JavaScript still liveinto browser...
  • 111.
    Dom access: write for (var i = 0; i < 100; i += 1) { document.getElementById("result").innerHTML += i + ", "; } {antipattern}
  • 112.
    Dom access: updatelocal variable var i, content = ""; for (i = 0; i < 100; i += 1) { content += i + ","; } document.getElementById("result").innerHTML += content; {pattern}
  • 113.
    Dom access: read varpadding = document.getElementById("result").style.padding, margin = document.getElementById("result").style.margin; {antipattern}
  • 114.
    Dom access: readwith local variable var style = document.getElementById("result").style, padding = style.padding, margin = style.margin; {pattern}
  • 115.
    Dom manipulation // 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); {antipattern}
  • 116.
    Dom manipulation // 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); {antipattern}
  • 117.
    Dom manipulation 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); {pattern}
  • 118.
    Dom manipulation 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); {pattern}
  • 119.
    Dom manipulation 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); {pattern}
  • 120.
    Dom manipulation 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); {pattern}
  • 121.
    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> Worst {antipattern}
  • 122.
    The place of<script> element <!doctype html> <html> <head> <title>My App</title> <script src="all_20110127.js"></script> </head> <body> ... </body> </html> {antipattern}
  • 123.
    The place of<script> element <!doctype html> <html> <head> <title>My App</title> </head> <body> ... <script src="all_20110127.js"></script> </body> </html> {pattern}
  • 124.
    It’s just abeginning...
  • 125.
  • 127.
    “Save it fora rainy day!”
  • 128.
    Check your codewith jslint.com
  • 131.
  • 132.
  • 133.
    # Assignment: var cubes, math, num, number, opposite, number = 42 race, square; opposite = true var __slice = Array.prototype.slice; number = 42; opposite = true; # Conditions: if (opposite) { number = -42 if opposite number = -42; } # Functions: square = function(x) { square = (x) -> x * x return x * x; }; # Objects: math = math = { root: Math.sqrt root: Math.sqrt, square: square square: square, cube: (x) -> x * square x cube: function(x) { return x * square(x); } }; race = function() { # Splats: var runners, winner; race = (winner, runners...) -> winner = arguments[0], runners = 2 print winner, runners <= arguments.length ? __slice.call(arguments, 1) : []; return print(winner, runners); };} JavaScript
  • 134.
    # Assignment: var cubes, math, num, number, opposite, number = 42 race, square; opposite = true var __slice = Array.prototype.slice; number = 42; opposite = true; # Conditions: if (opposite) { number = -42 if opposite number = -42; } # Functions: square = function(x) { square = (x) -> x * x return x * x; }; # Objects: math = math = { root: Math.sqrt root: Math.sqrt, square: square square: square, cube: (x) -> x * square x cube: function(x) { return x * square(x); } }; race = function() { # Splats: var runners, winner; race = (winner, runners...) -> winner = arguments[0], runners = 2 print winner, runners <= arguments.length ? __slice.call(arguments, 1) : []; return print(winner, runners); };} JavaScript
  • 135.
    # Assignment: var cubes, math, num, number, opposite, number = 42 race, square; opposite = true var __slice = Array.prototype.slice; number = 42; opposite = true; # Conditions: if (opposite) { number = -42 if opposite number = -42; } # Functions: square = function(x) { square = (x) -> x * x return x * x; }; # Objects: math = math = { root: Math.sqrt root: Math.sqrt, square: square square: square, cube: (x) -> x * square x cube: function(x) { return x * square(x); } }; race = function() { # Splats: var runners, winner; race = (winner, runners...) -> winner = arguments[0], runners = 2 print winner, runners <= arguments.length ? __slice.call(arguments, 1) : []; return print(winner, runners); };} JavaScript
  • 136.
    # Assignment: var cubes, math, num, number, opposite, number = 42 race, square; opposite = true var __slice = Array.prototype.slice; number = 42; opposite = true; # Conditions: if (opposite) { number = -42 if opposite number = -42; } # Functions: square = function(x) { square = (x) -> x * x return x * x; }; # Objects: math = math = { root: Math.sqrt root: Math.sqrt, square: square square: square, cube: (x) -> x * square x cube: function(x) { return x * square(x); } }; race = function() { # Splats: var runners, winner; race = (winner, runners...) -> winner = arguments[0], runners = 2 print winner, runners <= arguments.length ? __slice.call(arguments, 1) : []; return print(winner, runners); };} JavaScript
  • 137.
    # Assignment: var cubes, math, num, number, opposite, number = 42 race, square; opposite = true var __slice = Array.prototype.slice; number = 42; opposite = true; # Conditions: if (opposite) { number = -42 if opposite number = -42; } # Functions: square = function(x) { square = (x) -> x * x return x * x; }; # Objects: math = math = { root: Math.sqrt root: Math.sqrt, square: square square: square, cube: (x) -> x * square x cube: function(x) { return x * square(x); } }; race = function() { # Splats: var runners, winner; race = (winner, runners...) -> winner = arguments[0], runners = 2 print winner, runners <= arguments.length ? __slice.call(arguments, 1) : []; return print(winner, runners); };} JavaScript
  • 138.
    # Assignment: var cubes, math, num, number, opposite, number = 42 race, square; opposite = true var __slice = Array.prototype.slice; number = 42; opposite = true; # Conditions: if (opposite) { number = -42 if opposite number = -42; } # Functions: square = function(x) { square = (x) -> x * x return x * x; }; # Objects: math = math = { root: Math.sqrt root: Math.sqrt, square: square square: square, cube: (x) -> x * square x cube: function(x) { return x * square(x); } }; race = function() { # Splats: var runners, winner; race = (winner, runners...) -> winner = arguments[0], runners = 2 print winner, runners <= arguments.length ? __slice.call(arguments, 1) : []; return print(winner, runners); };} JavaScript
  • 139.
    # Existence: if (typeof elvis != "undefined" && elvis !== null) { alert "I knew it!" if elvis? alert("I knew it!"); } # Array comprehensions: cubes = (function() { cubes = (math.cube num for num in list) 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
  • 140.
    # Existence: if (typeof elvis != "undefined" && elvis !== null) { alert "I knew it!" if elvis? alert("I knew it!"); } # Array comprehensions: cubes = (function() { cubes = (math.cube num for num in list) 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
  • 141.
    ...but it’s justanother story...
  • 143.
    giordano.scalzo@cleancode.it @giordanoscalzo www.slideshare.net/giordano github.com/gscalzo Roma,5 marzo 2011 http://creativecommons.org/licenses/by-nc-sa/3.0/