Javascript Rocks

Introduction

Core Javascript Features

Plan

Javascript Object Notation (JSON)

var point = {x: 51, y: 42};
console.log(point.x, point.y);

Constructor

function Point(x, y) {
  return {
    x: x,
    y: y
  };
}

var point = Point(51, 42);
console.log(point);

Constructor with Method attempt

function Point(x, y) {
  return {
    x: x,
    y: y,
    print: function () {
      console.log(?????.x, ?????.y);
    }
  };
}

Constructor with Method

function Point(x, y) {
  var point = {};
  point.x = x;
  point.y = y;
  point.print = function () {
    console.log(point.x, point.y);
  };
  return point;
}

var point = Point(51, 42);
point.print();

new Operator

function Point(x, y) {
  this.x = x;
  this.y = y;
  this.print = function () {
    console.log(this.x, this.y);
  };
}

var point = new Point(51, 42);
point.print();

Private variable

function Point(x, y) {
  this.x = x;
  var y_ = y;
  this.print = function () {
    console.log(this.x, y_);
  };
}

var point = new Point(51, 42);
point.print();

Prototype

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype = {
  print: function () {
    console.log(this.x, this.y);
  }
};

var point = new Point(51, 42);
point.print();

Without Prototype

point

x y print move intersect ...
51 42 function function function ...
x y print move intersect ...
51 42 function function function ...
x y print move intersect ...
51 42 function function function ...
x y print move intersect ...
51 42 function function function ...
x y print move intersect ...
51 42 function function function ...

With Prototype

point

x y __proto__
51 42 Point.prototype
x y __proto__
51 42 Point.prototype
x y __proto__
51 42 Point.prototype
x y __proto__
51 42 Point.prototype
x y __proto__
51 42 Point.prototype
 

Point.prototype

print move intersect ... __proto__
function function function ... null

Pimp My Class with Helpers

Prototypal inheritance: Object.create

var Point = {
  x: 51,
  y: 42,
  print: function () {
    console.log(this.x, this.y);
  }
};

var point = Object.create(Point);
point.print();

Multi-Level Inheritance

var Point = {
  x: 51,
  y: 42,
  print: function () {
    console.log(this.x, this.y);
  }
};

var Point3D = Object.create(Point);
Point3D.z = 666;
Point3D.print = function () {
  console.log(this.x, this.y, this.z);
}

var point = Object.create(Point3D);
point.print();

Object.extend

var Point = {
  x: 51,
  y: 42,
  print: function () {
    console.log(this.x, this.y);
  }
};

var Point3D = Object.extend(Object.create(Point), {
  z: 666,
  print: function () {
    console.log(this.x, this.y, this.z);
  }
});

var point = Object.create(Point3D);
point.print();

Class Framework: JS.Class

var Point = new JS.Class({
  initialize: function(x, y) {
    this.x = x;
    this.y = y;
  },
  print: function() {
    console.log(this.x, this.y);
  }
});

var Point3D = new JS.Class(Point, {
  initialize: function(x, y, z) {
    this.callSuper(x, y);
    this.z = z;
  },
  print: function() {
    console.log(this.x, this.y, this.z);
  }
});

var point = new Point3D(42, 51, 666);
point.print();

Module,
Namespace?

Namespace

lib3d.jsvar Lib3D = { /* ... */ };
lib3d.particle.jsLib3D.Particle = { /* ... */ };
lib3d.collada.jsObject.extend(Lib3D, {
  ColladaLoader: { /* ... */ }
});
app.html<script src="lib3d.js"></script>
<script src="lib3d.collada.js"></script>
<script>Lib3D.ColladaLoader.load(...);</script>

Local Namespace

lib3d.js{
  /* ... */
}
app.jsvar Lib3D = require('lib3d.js');
function require(filename) {
  var file = download(filename);
  return eval(file);
}

Local Namespace: Asynchronous

app.jsrequire('lib3d.js', function (Lib3D) {
  Lib3D.ColladaLoader.load();
});
function require(filename, callback) {
  downloadAsync(filename, function (file) {
    callback(eval(file));
  });
}

LD_PRELOAD?

Situation

function ProcessChatMessage(message, source, channel) {
  // ... Game Code ...
}

Ugly Hack: Game Patching

function ProcessChatMessage(message, source, channel) {
  if (channel == COMBAT_LOG_CHANNEL) {
    // ... Extension Code ...
  }
  // ... Game Code ...
}

Hook Technique

var defaultProcessChatMessage = ProcessChatMessage;

ProcessChatMessage = function(message, source, channel) {
  if (channel == COMBAT_LOG_CHANNEL) {
    // ... Extension Code ...
  }

  defaultProcessChatMessage(message, source, channel);
};

Memoization
...What!?

Situation

username.mouseover(function () {
  var user = getUser(username.id);
  displayTooltip(user);
});
function getUser(id) {
  // Fetch Request (Synchronous only for the example!)
  return user;
}

Object as Cache

var cache = {};
function getUser(id) {
  if (id in cache) {
    return cache[id];
  }
  // Fetch Request
  return cache[id] = user;
}

Self-invoking Function

var getUser = (function () {
  var cache = {};
  return function (id) {
    if (id in cache) {
      return cache[id];
    }
    // Fetch Request
    return cache[id] = user;
  };
})();

Non-obstrusive

function memoize (f) {
  var cache = {};
  return function () {
    if (arguments in cache*) {
      return cache[arguments];
    }
    return cache[arguments] = f.apply(this, arguments);
  };
}

var getUser = memoize(function (id) {
  // Fetch Request
  return user;
});

Augment Function Prototype

Function.prototype.memoize = function () {
  var cache = {};
  var f = this;
  return function () {
    if (arguments in cache) {
      return cache[arguments];
    }
    return cache[arguments] = f.apply(this, arguments);
  };
}
function getUser (id) {
  // Fetch Request
  return user;
};
getUser = getUser.memoize();

Write Less, Do More
API Matters

Default Options

function fooo(options) {
  options = Object.extend({
    validate: false,
    limit: 10
  }, options);

  // Do something with the options
}

fooo();
fooo({validate: true});
fooo({validate: true, limit: 20});

Method Chaining

Example

$('<input>')
  .addClass('username')
  .val('Username')
  .click(function () { /* ... */ })
  .appendTo(login_form);

Implementation

function Fooo() {}
Fooo.prototype = {
  method: function () {
    // Do something
    return this;
  }
};

var obj = new Fooo();
obj.method().method();

Method Chaining Examples

HTML Document Traversing$('li')        // li: List Item
  .has('ul')   // ul: Unordered List
  .eq(1)
  .parent()
SQL QuerySQL.Select()
  .field('*')
  .table('users')
  .limit(10)
Asynchronous Tasks$('<div/>')
  .fetch('navigation.html') // Asynchronous
  .addClass('column')
  .appendTo('#side')

Polymorphism

Augment HTML Element$(username)
Augment Multiple HTML Elements$([username, password])
Create HTML Fragment$('<div/>')
CSS Query for HTML Element$('input[type=text].username')

Expected Features

Set properties$('<input/>', {type: 'text', class: 'user'})
Make the search relative$('input[type=text].username', login_form)
Execute function after page load$(function () { /* ... */ });

Keymaster

key('shift + a', '⌘ + alt + 1', function (event, handler) {
  console.log(handler.shortcut); // 'shift + a'
  console.log(handler.keys); // {shift: true, a: true}
});

Styleless

Variables

var color = '#4D926F'
Style({
  '#header': { 
    color: color
  },
  h2: {
    color: color
  }
})
#header {
  color: #4D926F;
}
h2 {
  color: #4D926F;
}

Styleless

Functions

function roundedCorners(radius) {
  return {
    'border-radius': radius,
    '-webkit-border-radius': radius,
    '-moz-border-radius': radius
  }
}
Style({
  '#header': Style(roundedCorners('15px'), {
    color: 'red'
  })
})
#header {
  border-radius: 15px;
  -webkit-border-radius: 15px;
  -moz-border-radius: 15px;
  color: red;
}

Pattern Matching with wu.match

Caml

let rec map f list =
  match list with
      []         -> []
    | head::tail -> f(head) :: map f tail

Javascript

function map(f, list) {
  return match(list,
    [ [] ]   , [],
    [ Array ], function (arr) {
      var head = arr[0], tail = arr.splice(1);
    return [f(head)].concat(map(f, tail));
    }
  );
}

map(function (n) { return n * n; }, [1, 2, 3, 4])
>> [1, 4, 9, 16]

Pattern Matching with wu.match

CoffeeScript

map = (f, list) ->
  match list, 
    [ [] ]   , [],
    [ Array ], (head, tail...) -> [f head].concat map f, tail

Caml

let rec map f list =
  match list with
      []         -> []
    | head::tail -> f(head) :: map f tail

Javascript People

Myself!

Famous Javascript Gurus

/

#