10 lessons from porting JavaScript to Dart

I wrote a simple HTML5 game for my Google IO 2011 Introduction to HTML5 Game Development talk. The original game was written in JavaScript, and as an exercise I just ported the game to Dart. This post contains some lessons learned from the process.

Background: The original JavaScript code for the game is open source, as is the new Dart code.

Note: The Dart version doesn't have sound working, as the Web Audio API isn't currently working in Dart.

Straight forward port

The executive summary of the porting process is: it's pretty dang easy. Dart had two primary design constraints: it must be familiar to JavaScript developers, and it must compile to JavaScript. Having just ported over 574 lines of JavaScript to 554 lines of Dart, I can say that those two design constraints are being satisfied quite well.

To be fair, I wrote the first JavaScript version with structured classes in mind (using JavaScript's prototype system) so it was easy to port to Dart's native classes.  For example, compare the following JavaScript class:

// old JavaScript code
function Timer() {
    this.gameTime = 0;
    this.maxStep = 0.05;
    this.wallLastTimestamp = 0;
}

Timer.prototype.tick = function() {
    var wallCurrent = Date.now();
    var wallDelta = (wallCurrent - this.wallLastTimestamp) / 1000;
    this.wallLastTimestamp = wallCurrent;
    
    var gameDelta = Math.min(wallDelta, this.maxStep);
    this.gameTime += gameDelta;
    return gameDelta;
}

to the new equivalent Dart class:

// new Dart code
class Timer {

  num gameTime = 0;
  static final num MAX_STEP = 0.05;
  num wallLastTimestamp = 0;
  
  num tick() {
    num wallCurrent = new Date.now().value;
    num wallDelta = (wallCurrent - wallLastTimestamp) / 1000;
    wallLastTimestamp = wallCurrent;
    
    num gameDelta = Math.min(wallDelta, MAX_STEP);
    gameTime += gameDelta;
    return gameDelta;
  }
}

I think the Dart class syntax is easier to read, and it's a welcome relief to be able to omit all of those this qualifiers that are required in JavaScript. Regardless, I think it's safe to say that a developer versed in one language can grok the other.

So what did I learn?

1) Types rule. I know there are a bunch of JavaScript developers that have never seen a static type, and don't want to touch them. To that I say, don't be too quick to judge. Types made the porting process much quicker, because my editor and compiler caught errors much earlier for me. Did I use types for everything? Nope, but I will be using types in Dart for most things.

2) The Dart editor is very handy. With it's ability to warn me of real and potential errors as I code, I'm able to code faster. I think web developers are going to have an epiphany when they realize what they've been missing: real-time analysis of their code as they write it is a real time saver and will result in better code.

3) Frog compiler generates very logical JavaScript. Dart code will run on modern browsers via JavaScript, and Frog is the Dart to JavaScript compiler that is currently recommended due to its terse and human readable output. The Frog compiler turned my Dart code back into what was essentially the same JavaScript code that I originally wrote.  For example, check out the following JavaScript code that came from Frog:

// this JavaScript code came from Frog
function Timer() {
  this.gameTime = 0
  this.wallLastTimestamp = 0
  // Initializers done
}
Timer.prototype.tick = function() {
  var wallCurrent = new DateImplementation.now$ctor().value;
  var wallDelta = (wallCurrent - this.wallLastTimestamp) / 1000;
  this.wallLastTimestamp = wallCurrent;
  var gameDelta = Math.min(wallDelta, 0.05/*Timer.MAX_STEP*/);
  this.gameTime += gameDelta;
  return gameDelta;
}

4) Dart doesn't let me get sloppy. Thanks to types, real classes, inheritance, modifiers like static and final, and real lexical scoping, my Dart code feels more robust. I'm not allowed to slack.

5) The api.dartlang.org docs are helpful. Thanks to the new API docs, and the Dart Editor, it was easy to find my way around the core libraries.

6) Math.abs() is now num.abs(). This makes more sense to me, I'm glad they did it. For example, instead of Math.abs(-4) you will write -4.abs().

7) Grab the number of milliseconds since the epoch with new Date.now().value. Slightly more verbose than JavaScript, but this might change as the whole core libraries are going to get some major love in the beginning of 2012.

8) Dart has one falsy value: false. This is such a blessing compared to JavaScript's six falsy values. This means I had to rewrite:

// JavaScript
if (something) {
  // do stuff
}

to:

// Dart
if (something != null) {
  // do stuff
}

9) Real lexical scoping means I can drop this from instance variables. I no longer needed to write this.foo in my objects, I could just reference foo.

10) console.log() is now print(). The new print() is more terse, and works both on the server via the VM and in the browser. I'll allow it!

Summary

If your JavaScript is half way logical, you'll probably find it very straight forward to convert to Dart. There are differences, but most of the work can be handled by some regular expressions and some search and replaces. The resulting code felt more stable, as types and the tools like the Dart Editor didn't allow me to slack.

Check out the resulting Dart code for a simple HTML5 game.
10 comments

Popular posts from this blog

  • Sponsor:  Register today for  New Game, the conference for HTML5 game developers . Learn from Mozilla, Opera, Google, Spil, Bocoup, Mandreel, Subsonic, Gamesalad, EA, Zynga, and others at this intimate and technically rich conference. Join us for two days of content from developers building HTML5 games today. Nov 1-2, 2011 in San Francisco.  Register now ! This is the second article in a Box2D series, following the Box2D Orientation article. The Box2DWeb port of Box2D contains a nice example to show off the basics of integrating physics simulations into your web app. This post will provide a walkthrough of the example, explaining the high level concepts and code. First, let's see the example in action. The code for the above is open source and available on GitHub. It was adapted from Box2DWeb's example . Animating Before we look at Box2D, it's important to understand how the above simulation is animated. You might think setInterval or setTimeout is
  • In which I port a snazzy little JavaScript audio web app to Dart , discover a bug, and high-five type annotations. Here's what I learned. [As it says in the header of this blog, I'm a seasoned Dart developer. However, I certainly don't write Dart every day (I wish!). Don't interpret this post as "Hi, I'm new to Dart". Instead, interpret this post as "I'm applying what I've been documenting."] This post analyzes two versions of the same app, both the original (JavaScript) version and the Dart version. The original version is a proxy for any small JavaScript app, there's nothing particularly special about the original version, which is why it made for a good example. This post discusses the differences between the two implementations: file organization, dependencies and modules, shims, classes, type annotations, event handling, calling multiple methods, asynchronous programming, animation, and interop with JavaScript libraries. F
  • Angular and Polymer, sitting in a DOM tree, B-i-n-d-i-n-g. First comes components, Then comes elements, Then comes the interop with the node dot bind. Angular , a super heroic MVC framework, and Polymer , polyfills and enhancements for custom elements built on top of Web Components, can live harmoniously in the same app. This post shows you how to connect Angular-controlled components to Polymer-controlled elements via data binding. And we do it all in Dart . Angular and Polymer I get asked "Should I use Angular or Polymer?" a lot. My answer is, "Yes". That is, both libraries have distinct strengths, and you can use both in the same app. Polymer excels at creating encapsulated custom elements. You can use those custom elements in any web app or web page, regardless if that app is built with Angular, Ember, etc. Angular excels at application engineering, with dependency injection, end-to-end testability, routing, and services. Here are som