JavaScript, the Good Parts

Thomas Pedersen, Miles AS

Instructions

  • Use arrow keys to switch slides
  • Use ctrl+r to run the code samples
  • Use ctrl+i to focus the code editor
  • Use esc to blur the code editor or to show the slide overview
  • Enjoy!

JS had to 'look like Java' only less so, be Java’s dumb kid brother or boy-hostage sidekick. Plus, I had to be done in ten days or something worse than JS would have happened

- Brendan Eich, 2010

The Bad Parts

  • Global variables
  • Scope
  • Semicolon insertion
  • + adds and concatenates
  • typeof
  • with and eval
  • Phony arrays
  • undefined, NaN
  • == and !=

The Good Parts

  • Functions as first class objects
  • Dynamic objects with prototypal inheritance
  • Object literals
  • Array literals
  • Loose typing

JavaScript Types

  • Object
  • String
  • Boolean
  • Array
  • Date
  • RegExp

Objects

  • An object is a dynamic, mutable collection of properties
  • Every property has a key string that is unique within that object
  • Prototype linkage feature, that allows one object to inherit properties of another

set, get and delete

var object = {};

object.name = 'Gob';
object['name'] = 'Gob';
console.log(object.name);

var name = object.name;
var name = object['name'];
console.log(name);

delete object.name;
delete object['name'];
console.log(object.name);

Object Literals

  • An expressive notation for creating objects
var emptyObject = { };

var myObject = {
  'some property': 'some value',
  foo: emptyObject,
  method: function(param) {
    // do something
  }
};

Prototype

  • Every object is linked to a prototype object from which it can inherit properties
  • All objects created from object literals are linked to Object.prototype

Prototype

var Person = function (name) {
  this.name = name;
};

Person.prototype.sayHello = function () {
  console.log("Hello, I'm " + this.name);
};

var tobias = new Person('Tobias');
tobias.sayHello();

Prototype

var Person = function (name) {
  this.name = name;
};

Person.prototype.sayHello = function () {
  console.log("Hello, I'm " + this.name);
};

var tobias = new Person('Tobias');
tobias.sayHello();

Person.prototype.sayMyName = function () {
  console.log('My name is ' + this.name);
};
tobias.sayMyName();

Prototype Chaining

var Person = function () { };
Person.prototype.sayHello = function () {
  console.log("Hello, I'm " + this.name);
};

var Bluth = function (name) {
  this.name = name;
};

Bluth.prototype = new Person();

var bluth = new Bluth('Buster');
bluth.sayHello();

Numbers

  • Only one number type
  • double-precision 64-bit format IEEE 754 values
  • Math namespace for advanced operations

parseInt

console.log(parseInt('123', 10));
console.log(parseInt('010', 10));
console.log(parseInt('hello'));

Strings

  • Sequences of unicode characters (16 bit)
  • Similar strings are equal
  • Strings are objects
  • Strings are immutable

Booleans

  • true or false
  • 0, "", NaN, null and undefined are falsy
  • Everything else is truthy
  • Boolean operations: &&, || and !

Arrays

  • A special type of object; Keys are whole numbers
  • No need to provide type or length when creating an array

Array Literals

  • An expressive notation for creating arrays
var array = [
  'one', 
  'two', 
  { number: 'three' }, 
  function() {}
];

Dates

var date = new Date('November 15, 2012');

console.log(date.getFullYear());
console.log(date.getMonth());
console.log(date.getDate());

RegExp

var str = 'me@example.com';
var pattern = /[\w-]+@[\w-]+\.+[\w-]+/i;

console.log(str.match(pattern));
console.log(pattern.test(str));

Functions

Functions are First Class Objects

  • May be passed as an argument to a function
  • May be returned from a function
  • May be assigned to a variable
  • May be stored in an object or array

A Function has Four Parts

  • the reserved word function
  • optional name (which can be used to call itself in recursion)
  • optional parameters
  • set of statements wrapped in curly braces
var add = function(a, b) {
    return a + b;
};

function add(a, b) {
    return a + b;
}

Invocation Patterns

  • Method invocation pattern
  • Function invocation pattern
  • Constructor invocation pattern
  • Apply invocation pattern

These patterns differ in how this is initialized

Method Invocation Pattern

  • When a function is stored as a property of an object, it is called a method
  • When a method is invoked, this is bound to that object (late binding)
var myObject = {
  value: 2,
  increment: function(inc) {
    this.value += inc;
    console.log(this);
    console.log(this.value);
  }
};

myObject.increment(2);

Function Invocation Pattern

  • When the function is not the property of an object, it is called a function
  • When a function is invoked, this is bound to the global object
var foo = function() {
  console.log(this);
};

foo();

Function Invocation Pattern

var myObject = {
  value: 1,
  double: function() {

    var helper = function() {
      console.log(this.value);
    };
      
    helper(); // invoke as a function
  }
};

myObject.double(); // invoke as a method

Constructor Invocation Pattern

  • If a function is invoked with the new prefix, a new object will be created with a hidden link to the value of the function's prototype member
  • this is bound to that new object
  • By convention, they are kept in variables with a capitalized name
var Foo = function(val) {
  this.value = val;
  console.log(this.value);
  console.log(this);
};

var myFoo = new Foo('confused');
var myFoo = Foo('confused');

Apply Invocation Pattern

  • The apply or call method lets us choose the value of this
var add = function(a, b) {
  console.log(a + b + this.c);
};

var context = {
  c: 5
};

add(3, 4);
add.apply(context, [3, 4]);
add.call(context, 3, 4);

arguments

  • In addition to this, all functions get a bonus parameter arguments
  • It holds all arguments passed to the function, including those that where not assigned to a parameter
var sum = function(){
  var i, sum = 0;
  for(i = 0; i < arguments.length; i += 1){
    sum += arguments[i];
  }
  return sum;
};

console.log(sum(3, 7, 9, 23));

Hoisting

var myVar = 'my value';

function bar() {
  console.log(myVar);
  var myVar = 'local value';
}

bar();

Hoisting

var myVar = 'my value';

function bar() {
  var myVar = undefined;
  console.log(myVar);
  myVar = 'local value';
}

bar();

Hoisting

console.log(subtract(2, 3));
function subtract(a, b) {
  return a - b;
}

Hoisting Takeaway

  • Declare all variables at the top of your function
  • Declare all functions before you call them

Scope

var foo = 1;

function bar() {
  if (!foo) {
    var foo = 10;
  }
  console.log(foo);
}

bar();

Scope

var foo = 1;

function bar() {
  var foo = undefined;
  if (!foo) { // truthy
    foo = 10;
  }
  console.log(foo);
}

bar();

Global Scope

function bar() {
  x = 10;
}
bar();
console.log(x);


function foo() {
  var y = 10;
}
foo();
console.log(y);

GLOBAL VARIABLES
ARE EVIL!

Scope Takeaway

  • JavaScript has function scope, not block scope
  • If you forget the var keyword, you create a global reference
  • Never forget the var keyword

Closure

function foo(x) {
  var tmp = 3;

  function bar(y) {
    console.log(x + y + (++tmp));
  }

  bar(10);
}

foo(2);
foo(2);
foo(2);
foo(2);

Closure

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + (++tmp));
  }
}

var bar = foo(2);
bar(10);
bar(10);
bar(10);
bar(10);

Closure Takeaway

  • A closure in JavaScript is like keeping a copy of the all the local variables, just as they were when a function exited.

Patterns

Module Pattern

(function (root) {
  var privateVariable;
  function privateFunction(x) {
    privateVariable = x;
  }

  root.firstMethod = function (y) {
    privateFunction(y);
  };

  root.secondMethod = function () {
    console.log(privateVariable);
  };
}(this));

this.firstMethod(10);
this.secondMethod();

Revealing Module Pattern

var singleton = (function () {
  var privateVariable;
  function privateFunction(x) {
    privateVariable = x;
  }

  return {
    firstMethod: function (y) {
      privateFunction(y);
    },
    secondMethod: function () {
      console.log(privateVariable);
    }
  };
}());

singleton.firstMethod(10);
singleton.secondMethod();

Sandboxing

(function (window, undefined) {
  var document = window.document,
      $ = window.jQuery.noConflict();

  var root = {};

  // ...

  window.root = root;
} (window));

Styles

Style is Not Subjective

var foo = function(){
  return {
    ok: true
  };
};

console.log(foo().ok);

Style is Not Subjective

  • Use === and !==, not == or !=
  • Declare all variables and functions before use
  • Always use blocks with structured statements such as if and while
  • Variables and functions should start with a lower case letter
  • Constructor functions which must be used with the new prefix should start with a capital letter
  • Use {} instead of new Object(). Use [] instead of new Array()
  • eval is Evil
  • Don't use with
  • http://javascript.crockford.com/code.html

JSLint || JSHint

  • A JavaScript program that looks for problems in JavaScript programs
  • It is a code quality tool

Trivia

var trivia = Array(8).join('hi' + 1);

console.log(trivia);

JavaScript, the Good Parts

Thomas Pedersen, Miles AS