Intro to Advanced JavaScript




                  By Ryan Stout
About Me
Ryan Stout




agileproductions.com
github.com/ryanstout
Programming
JavaScript for
  14 Years
ExceptionHub
JavaScript
 History
JavaScript
      History
Developed by Brendan Eich in
           1995
JavaScript
      History
Originally called Mocha, then
          LiveScript
JavaScript
     History
Inspired By Scheme and Self
JavaScript
 History
Used C/Java Syntax
JavaScript
      History
Renamed JavaScript to Play off
            Java
JS Fun
not really a number

typeof NaN
//=> number
Topics


http://www.flickr.com/photos/arndalarm/354835622/sizes/l/in/photostream/
Topics
 Concepts
Topics
Frameworks
Topics
Tools/Debugging
Topics
 Testing
Concepts
Types
Primitives
var   myFloat = 5;
var   myFloat2 = 5.0;
var   myString = 'String';
var   myBoolean = true;
var   myNull = null;
var   myUndefined = undefined;
Wrappers
var wFloat = new Number(42.0);
var wString = new String('Hello');
var wBoolean = new Boolean(true);
Container Types
// Wrong Syntax
var myArray = new Array();
var myObject = new Object();

// Correct Syntax
var myArray = [];
var myObject = {};
Dynamic Typing

var pies = 3;

alert("We have " + pies + " pies");
// We have 3 pies
Dynamic Typing
var pies = '5';

// eat 2
pies = pies - 2;

alert('you have '+pies+' pies');
// you have 3 pies
Dynamic Typing
var pies = '5';

// bake 4
pies = pies + 4;

alert('you have '+pies+' pies');
// you have 54 pies
JS Fun
magically changing number
   var a = 9999999999999999
   console.log(a);
   //=> 10000000000000000
Objects
var stateCapitals = {};
stateCapitals['Oregon'] = 'Salem';
stateCapitals['Montana'] = 'Helena';

stateCapitals['Montana']; // Helena
Objects
var stateCapitals = {};
stateCapitals.Oregon = 'Salem';
stateCapitals.Montana = 'Helena';

stateCapitals.Montana; // Helena
Objects
var stateCapitals = {
   'Oregon': 'Salem',
   'Montana': 'Helena'
};

stateCapitals['Montana']; // Helena
stateCapitals.Montana; // Helena
Objects (Hash Buckets)

var states = new Number(50);
states.Oregon = 'Salem';
states.Montana = 'Helena';

states.Montana; // Helena
console.log("We have "+states+" states");
// We have 50 states
Object Iteration
// Object Iteration
for (var state in states) {
  console.log(states[state] + ' is the
  capital of ' + state);
}
// Helena is the capital of Montana
// ...
Object Iteration
// Object Iteration
for (var state in states) {
  if (states.hasOwnProperty(state)) {
    console.log(states[state] + ' is the
    capital of ' + state);
  }
}
// Helena is the capital of Montana
Functions
•First Class
• Lexical Scope
• Prototypal
• Closure
Methods
Methods are just
functions that are
assigned to the property
of an object.
Functions
function concat(stringsArray) {
  return stringsArray.join('');
}

concat(['Cat', 'Fish']); // CatFish
First Class
var concat = function(stringsArray) {
  return stringsArray.join('');
}

concat(['Cat', 'Fish']); // CatFish
Functions
sayHi(); // hi

function sayHi() {
  console.log('hi');
}

sayHi(); // hi
First Class
sayHi(); // ReferenceError: sayHi is
         // not defined

var sayHi = function() {
  console.log('hi');
}

sayHi(); // hi
Lexical Scope
function saySomething() {
  var message = 'Lexical Scoped';
  console.log(message);
}

saySomething(); // Lexical Scoped
console.log(message); //
ReferenceError: message is not
defined
Lexical Scope
function sayDirection(up) {
  if (up == true) {
    var direction = 'up';
  } else {
    var direction = 'down';
  }
  console.log(direction);
}
sayDirection(true); // up
sayDirection(false); // down
Methods
var Singleton = {
   setCount: function(count) {
      this.count = count;
   },
   increment: function() {
      this.count++;
   }
};

Singleton.setCount(5);
Singleton.count; // 5
Singleton.increment();
Singleton.count;// 6
Closures
A "closure" is an expression (typically a
function) that can have free variables
together with an environment that binds
those variables (that "closes" the
expression).
Closures
function makeAdder(aNum) {
  var addNumFunc = function(bNum) {
     return aNum + bNum;
  };

    return addNumFunc;
}

var addFive = makeAdder(5);
console.log(addFive(2)); // 7
console.log(addFive(10)); // 15
$(function() {
  var links = $('a');

  // loop through each link
  links.each(
    function(link, index) {

           // bind the click event
           $(link).click(
              function(event) {
                console.log('clicked link #'+index
                +' of ' + links.length);
              }
           );
       }
  );
});
$(function() {
  var links = $('a');

  // loop through each link
  links.each(
    function(link, index) {

           // bind the click event
           $(link).click(
              function(event) {
                console.log('clicked link #'+index
                +' of ' + links.length);
              }
           );
       }
  );
});
$(function() {
  var links = $('a');

  // loop through each link
  links.each(
    function(link, index) {

           // bind the click event
           $(link).click(
              function(event) {
                console.log('clicked link #'+index
                +' of ' + links.length);
              }
           );
       }
  );
});
$(function() {
  var links = $('a');

  // loop through each link
  links.each(
    function(link, index) {

           // bind the click event
           $(link).click(
              function(event) {
                console.log('clicked link #'+index
                +' of ' + links.length);
              }
           );
       }
  );
});
$(function() {
  var links = $('a');

  // loop through each link
  links.each(
    function(link, index) {

           // bind the click event
           $(link).click(
              function(event) {
                console.log('clicked link #'+index
                +' of ' + links.length);
              }
           );
       }
  );
});
Closures
function addLinks() {
  for (var i=0, link; i<5; i++) {
    link = document.createElement("a");
    link.innerHTML = "Link " + i;
    link.onclick = function () {
       console.log(i);
    };
    document.body.appendChild(link);
  }
}
window.onload = addLinks;
Closures
function addLinks() {
  for (var i=0, link; i<5; i++) {
    link = document.createElement("a");
    link.innerHTML = "Link " + i;
    link.onclick = function (num) {
      return function () {
         alert(num);
      };
    }(i);
    document.body.appendChild(link);
  }
}
Private Variables
var person = function () {
  // Private
  var name = "Ryan";
  return {
     getName : function () {
        return name;
     },
     setName : function (newName) {
        name = newName;
     }
  };
}();
alert(person.name); // Undefined
alert(person.getName()); // "Ryan"
person.setName("Brendan Eich");
alert(person.getName()); // "Brendan Eich"
Prototyping __proto__
var states = {
   'Montana': 'Helena',
   'Oregon': 'Salem'
};

var statesAndOthers = {
  'Guam': 'Hagatna',
  'Puerto Rico': 'San Juan'
}

statesAndOthers['__proto__'] = states;
statesAndOthers['Montana']; // Helena
statesAndOthers['Puerto Rico']; // San Juan
Prototyping __proto_

statesAndOthers        states          Object
 Guam    Hagatna   Montana Helena
 Puerto San Juan    Oregon     Salem
  Rico
__proto__          __proto__
One Problem
var states = {
   'Montana': 'Helena'
   'Oregon': 'Salem'
};

var statesAndOthers = {
  'Guam': 'Hagatna',
  'Puerto Rico': 'San Juan'
}

statesAndOthers['__proto__'] = states;
statesAndOthers['Montana']; // Helena
statesAndOthers['Puerto Rico']; // San Juan
Prototyping
function extendedObject(o) {
  var objFunc = function() {};
  objFunc.prototype = o;
  return new objFunc();
}

var states = {
   'Montana': 'Helena',
   'Oregon': 'Salem'
};

var statesAndOthers = extendedObject(states);
statesAndOthers['Guam'] = 'Hagatna';
statesAndOthers['Puerto Rico'] = 'San Juan';

statesAndOthers['Guam']; // Hagantan
statesAndOthers['Montana']; // Helena
Prototyping
// Constructor
function Animal(name) {
  this.name = name;
}

Animal.prototype.speak = function() {
   console.log("Hi, I'm " + this.name);
};

Animal.prototype.isAlive = function() {
   return true;
};

var bob = new Animal('Bob');
bob.speak(); // Hi, I'm Bob
bob.isAlive(); // true
Prototyping
// Constructor
function Animal(name) {
  this.name = name;
}

Animal.prototype = {
  speak: function() {
     console.log("Hi, I'm " + this.name);
  },

     isAlive: function() {
       return true;
     }
};

var bob = new Animal('Bob');
Animal Function Setup

  Animal (func)   Animal.prototype   Object
prototype          Speak      func
                   isAlive    func

                  __proto__
Animal Instance Bob

       bob           Animal.prototype   Object
   name      Bob      Speak      func
__proto__
constructor Animal    isAlive    func

                     __proto__
Prototyping
// Constructor
function Dog(name) {
  this.name = name;
}

Dog.prototype = new Animal();

Dog.prototype.speak = function() {
   console.log("Bark");
};

Dog.prototype.rollOver = function() {
   console.log('rolling over...');
};

var rover = new Dog('Rover');
rover.speak(); // Bark
rover.isAlive(); // true
Dog Class
   Dog (func)        prototype (Dog)
prototype             name      undef
                      Speak      func
                    rollOver     func
                   __proto__

                    Animal.prototype    Object
                     Speak      func
                     isAlive    func

                   __proto__
Rover
       rover              Dog.prototype
   name        Rover   (from new Animal())
                          name     undef
__proto__                 Speak     func
                        rollOver    func
                       __proto__

Animal.prototype
                       Object
 Speak      func
 isAlive       func
__proto__
this
Calling from this
     var EventTracker = {
     count: 0,
     sayCount: function() {
        console.log(this.count, ' times');
     },

     track: function() {
       this.count += 1;
       this.sayCount();
     }
};

EventTracker.track(); // 1 times
EventTracker.track(); // 2 times
More Closure
  var EventTracker = {
   count: 0,
   track: function() {
     var that = this;
     $.get('/track', function(data) {
       that.count += 1;
       console.log(that.count, ' times');
     });
   }
};
Setting this
     var obj1 = {
     name: 'Object 1'
     sayName: function(before, after) {
       console.log(before + this.name + after);
     }
};

var obj2 = {
   name: 'Object 2'
};

obj1.sayName('hi ', '.'); // hi Object1.
obj1.sayName.call(obj2, 'hi', '.'); // hi Object2
obj1.sayName.apply(obj2, ['hi', '.']); // hi Object2
More Closure
     var EventTracker = {
     count: 0,
     callBack: function(data) {
        this.count += 1;
        console.log(this.count, ' times');
     },

     track: function() {
       var that = this;
       $.get('/track', function() { that.callBack() });
     }
};
More Closure
 var EventTracker = {
 count: 0,
 callBack: function(data) {
    this.count += 1;
    console.log(this.count, ' times');
 },

     track: function() {
       var that = this;
       $.get('/track', this.callback.bind(this));
     }
};
Bind Function
    if (!Function.prototype.bind) {
    Function.prototype.bind = function(scope) {
      var func = this;

         return function() {
            return func.apply(scope, arguments);
         };
    };
}
JS Fun
       length of what

console.log((!+[]+[]+![]).length);
//=> 9
Tools


http://www.flickr.com/photos/lenore-m/2515800654/sizes/o/
Compression
Compression
   JSMin
Compression
Yahoo Compressor
Compression
Google Compressor
Debugging
Firebug - Console
Firebug - HTML
Firebug - CSS
Firebug - Script
Firebug - DOM
Firebug - Net
Google Chrome
Safari
Opera Dragonfly
IE - Developer Toolbar
Other IE Tools


Visual Studio
Microsoft Script Editor (Office)
Microsoft Script Debugger (Free)
Testing




http://stopbuyingrotas.wordpress.com/2008/10/
JSpec
 R.I.P.
Jasmine
    The New Hotness

http://pivotal.github.com/jasmine/
Jasmine
Behavior Driven Development
Runs in the Browser
Ruby Gem to Automate Tests
Jasmine
describe("javascript", function() {
  it('should increment a variable', function () {
    var foo = 0;
    foo++;

    expect(foo).toEqual(1);
  });
});
Jasmine
describe("spy behavior", function() {
  it('should spy on an instance method', function() {
    var obj = new Klass();
    spyOn(obj, 'method');
    obj.method('foo argument');

    expect(obj.method).toHaveBeenCalledWith('foo 
    argument');

    var obj2 = new Klass();
    spyOn(obj2, 'method');
    expect(obj2.method).not.toHaveBeenCalled();
  });
});
JS Fun
       bad math

console.log(0.1 + 0.2)
//=> 0.30000000000000004
Frameworks


http://jinja.apsara.org/travels/2005_03_battambang/phnom_sampow.htm
Frameworks
The bad news is JavaScript
is broken, the good news is
we can fix it with JavaScript
               - anonymous
JQuey
• DOM Wrapper
• Event Binding
• Ajax (XMLHttpRequest)
Underscore.js
List comprehension library
Underscore.js
                     Map


_.map([1, 2, 3], function(n){ return n * 2; });
_([1, 2, 3]).map(function(n){ return n * 2; });
// [2, 4, 6]
Underscore.js
                        Each

_.each([1, 2, 3], function(num) { alert(num); });
// alerts each number in turn...
_.each({one : 1, two : 2, three : 3},
   function(num, key){ alert(num); }
);
// alerts each number in turn...
Underscore.js
                      Reject


_.reject([1, 2, 3, 4, 5, 6], function(num) {
    return num % 2 == 0;
});
=> [1, 3, 5]
Underscore.js
• Also
 • include, any, max, min, first, select,
    indexOf, etc...
  • very useful
Reading
One More Thing

            CoffeeScript


http://www.flickr.com/photos/swarmoeskerken/3382771943/sizes/l/
CoffeeScript
# Assignment:              var list, number,
number   = 42              opposite, square;
opposite = true
                           number = 42;
# Conditions:              opposite = true;
number = -42 if opposite   if (opposite) {
                              number = -42;
# Functions:               }
square = (x) -> x * x      square = function(x) {
                              return x * x;
# Arrays:                  };
list = [1, 2, 3, 4, 5]     list = [1, 2, 3, 4, 5];
CoffeeScript
                                 var square;
                                 math = {
# Objects:
                                    root: Math.sqrt,
math =
                                    square: square,
  root:    Math.sqrt
                                    cube: function(x) {
  square: square
                                      return x * square(x);
  cube:    (x) -> x * square x
                                    }
                                 };
# Splats:
race = (winner, runners...) ->
  print winner, runners
                                  Coffee
# Existence:
alert "I knew it!" if elvis?
                                  Script
race = function() {
   var runners, winner;
   winner = arguments[0], runners = 2 <=
arguments.length ? __slice.call(arguments, 1) : [];
   return print(winner, runners);
};
if (typeof elvis != "undefined" && elvis !== null) {
   alert("I knew it!");
}
CoffeeScript
      # Array comprehensions:
      cubes = (math.cube num for num in list)




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

3 > 2 > 1 // false
Questions

agileproductions.com
ryan@agileproductions.com
References
http://www.slideshare.net/danwrong/
metaprogramming-javascript
http://robertnyman.com/2008/10/09/explaining-
javascript-scope-and-closures/
http://www.bennadel.com/blog/1482-A-
Graphical-Explanation-Of-Javascript-Closures-
In-A-jQuery-Context.htm

Intro to Advanced JavaScript