Learning To Love JavaScript
Past, Present, and Future

Alex Russell (slightlyoff@google.com)
May 11, 2011

Feedback: http://goo.gl/GSdOE
Twitter: #Chrome

Agenda

The little functional language that could

JavaScript is everywhere your app wants to be

Key concepts

Read JavaScript Top-to-Bottom

Functional? Object Based?

Everything's An Object

{} instanceof Object; // true
[] instanceof Object; // true
(function(){}) instanceof Object; // true

...And All Objects Act Like Maps...

var obj = {
  item: "value"
};
obj.item === obj["item"];
// Don't need eval! Even for builtins:
Array["forEach"].call([], function(){});

...Even Arrays

var list = ["howdy", "pardner"];
list["otherGreeting"] = "hola";

list[0] === "howdy";
list["otherGreeting"] === "hola";

And It's All Mutable!

var obj = {
  item: "value"
};

obj.item = "replacement value";
// Objects are runtime extensible by default
obj.item2 = "another value";

// Properties can even be removed
delete obj.item;
console.log(obj.item); // undefined

Closures: The Other Side of Classes

// Classes are state with functions
function Behavior(config) {
  this.config = config;
  this.doIt = function(itemToActOn) {
    if (this.config.flag) {
      // ...
    } else {
      // ...
    }
  }
}

var b = new Behavior({ flag: false });
b.doIt($("item"));

Closures: The Other Side of Classes

// Closures are functions with state
function bindBehavior(config) {
  return function() {
    if (config.flag) {
      // ...
    } else {
      // ...
    }
  }
}

var b = bindBehavior({ flag: false });
b($("item"));

Prototypes, not inheritance!

var delegate = { item: "shadow value" };

// ES 5 builds in delegation via Prototypes
var obj = Object.create(delegate, { item: { value: "value" }});
var obj2 = Object.create(delegate);

obj.item == "value";
delegate.item == "shadow value";
obj2.item == "shadow value";

delegate.item = "new shadow value";
obj2.item == "new shadow value";

obj2.item = "even newer!";
delegate.item == "new shadow value";

Understanding "."

var obj = {
  item: "value"
  method: function() {
    console.log(this.value);
  }
};

// property de-reference
obj.item === obj["item"];

// scope modifier
obj.method() === obj["method"].call(obj);

this is dynamic!

Mixins for Instance-Level Composition

var counter = 0;
function Tracked() { this.id = counter++;  }
function Logged() { this.log = function() { console.log(this.id); }; }

function ItemType() { Tracked.call(this); Logged.call(this); }
ItemType.prototype = 
  Object.create(null, { type: { value: "generic" }});

function SubItemType() { ItemType.call(this); }
SubItemType.prototype = 
  Object.create(ItemType.prototype, { type: { value: "sub" }});

var i = new ItemType();     // i.id == 0;  i.type == "generic";
var i2 = new ItemType();    // i2.id == 1; i.type == "generic";
var si = new SubItemType(); // si.id == 2; si.type == "sub";

Remember mutability? And prototypes?

// Arrays have mutable prototypes...so lets fix things
Array.prototype.clear = function() {
  this.length = 0; return this;
}
Array.prototype.clone = function() {
  return this.concat();
}
[1, 2, "thinger"].clone().clear().length == 0;

Whoa...you mean we can change anything?

Waaaaait a minute...
Isn't this a maintainability nightmare?

How about modularity?

(function(){
  var local = "this is local, thanks to var";

  nonLocal = function() { // note, no "var"
    // an "exported" function can still see the local
    console.log(local);
    return local;
  }
})();

typeof nonLocal == "function";
typeof nonLocal() == "string";

How about modularity?

goog.provide('goog.ui.tree.TreeControl');

goog.require('goog.events.EventType');
goog.require('goog.events.FocusHandler');
goog.require('goog.events.KeyHandler');
goog.require('goog.ui.tree.BaseNode');
goog.require('goog.ui.tree.TreeNode');
goog.require('goog.ui.tree.TypeAhead');

The tower of Babel

goog.ui.tree.TreeControl = ...;
define("dijit/Tree",
  ["dojo",
   "dijit",
   "text!dijit/templates/TreeNode.html",
   "text!dijit/templates/Tree.html",
   "dijit/_Widget",
   "dijit/_Container",
   "dijit/_Contained"],
    function(dojo, dijit) {
      dojo.declare("dijit.Tree", ... );
      return dijit.Tree;
    });

Harmony to the rescue!

// goog/ui/tree/TreeControl.js
module Events = require('goog/events');
import Events.{EventType, FocusHandler, KeyHandler};

module Tree = require('goog/ui/tree');
import Tree.{BaseNode, TreeNode, TypeAhead};

export TreeControl = ...;

Classes?

goog.inherits(Actor, PacMan);
PacMan = Class.create(Actor, ... );
PacMan = Actor.extend(...);
dojo.define("PacMan", [Actor], ...);

What we mean

class Actor {
  new() { ... }
  moveTo(pos) { ... }
}

class PacMan : Actor {
  new() { ... }
  eat() { ... }
}

"De-sugaring"

// new-skool
class Actor {
  new() { ... }
  moveTo: function(pos){ ... }
}
// old-skool
function Actor() { ... }
Actor.prototype.moveTo = function (pos) { ... };
function PacMan() { }
PacMan.prototype = (function() {
    function tempCtor() {};
    tempCtor.prototype = Actor.prototype;
    var proto = new tempCtor();
    proto.constructor = PacMan;
    return proto;
  })();
PacMan.prototype.eat = function() { ... };

More de-sugaring

// new-skool
String.prototype.printf = function(format='', ...params) {
  // ...
}
// old-skool
String.prototype.printf = function(format) {
  format = format || '';
  var params = Array.prototype.slice.call(arguments, 2);
  // ...
}

Introducing Traceur


          

Traceur

REPL

What can we do with Traceur Compiler?

The Path to Harmony

Questions? Thanks!

code.google.com/p/traceur-compiler/

Feedback: http://goo.gl/GSdOE
Twitter: #Chrome