StageXL - ActionScript to Dart comparison

ActionScript to Dart comparison

Coming from ActionScript and learning Dart is easy. Both languages are class-based, single inheritance, object-oriented languages with C-style syntax. In many cases the syntax is identical or similar, often the Dart syntax is a little shorter and looks less verbose. As a bonus the Dart IDE is based on Eclipsed (but still light weight) and therefore familiar to many ActionScript developers. The Editor helps to write your code by providing autocomplete suggestions, refactoring, code navigation and many other handy tools you are used too. Launch your application directly from the Editor and set breakpoints to debug your code. Now take a look at the list below to learn how to map ActionScript code to Dart code.

Getting started

Classes

public class Spaceship extends Sprite {
    // fields, constructors, functions
}
class Spaceship extends Sprite {
    // fields, constructors, methods
}

Variables

var i:int = 1;
var s:String = 'hello world';
var n:Number = 3.14;
var b:Boolean = true;

// Warning: variable 'x' has no type declaration
var x; 
var p = new Point(1, 2);
int i = 1;
String s = 'hello world';
num n = 3.14;
bool b = true;

// No warning because Dart is dynamically typed.
var x;  
var p = new Point(1, 2);

Access modifiers

public var name:String; 
private var _text:String;
internal var _code:String;

// there is no match for the 'protected' modifier.
 
 
String name;     // public
String _text;    // private  (see comment below)
String _code;    // internal (see comment below)

// The underscore convention is the access modifier!
// The field or method is accessible everywhere in the
// current library, but hidden to other libraries.

Functions

public function echo(text:String):String {
  return text;
}

private function foo(i:int, s:String):void {
}
String echo(String text) {
  return text;
}

void _foo(int i, String s) {
}

Constructors

public class Sample {
  public function Sample(i:int, s:String) {
    // constructor code
  }
}    

public class Point {
  public var x:int, y:int;
  public function Point(x:int, y:int) {
    this.x = x;
    this.y = y;
  }
}
class Sample {
  Sample(int i, String s) {
    // constructor code
  }
}    

class Point {
  int x, y;
  Point(this.x, this.y);  // Automatic field initialization.
}

// Dart also supports 'named' and 'factory' constructors.
// http://www.dartlang.org/docs/language-tour

Getters and Setters

private var _name:String; 
    
public function get name():String {
  return _name;
}

public function set name(value:String): void {
  _name = value;
}
String _name;
  
String get name {
  return _name;
}
  
set name(String value) {
  _name = value;
}

Interfaces

public interface ISelectable {
  function select(var filter:String):void;
}

// A class implements interfaces.

public class Table implements ISelectable {
  public function select(var filter:String):void {
    // ...
  }
}
abstract class Selectable {
  void select(String filter);
}

// A class implements other classes' interfaces.

class Table implements Selectable {
  void select(String filter) {
    // ...
  }
}

Type Check and Cast

var o:Object = "hello";

if (o is String) { 
  var u:String = String(o).toUpperCase();
  trace(u);
}
var o = "hello";

if (o is String) {
  String u = (o as String).toUpperCase();
  print(u);
}

Collections

Arrays and Vectors

var a:Array = new Array();
var b:Array = [1, 2, 3];

var v:Vector.<String> = new Vector.<String>();
v.push('hello');
v.push('world');
var a = new List();
var b = [1, 2, 3];

var v = new List<String>();
v.add('hello');
v.add('world');

Queues and Stacks

var q:Array = new Array();  
q.push('hello');
q.push('world');

var f:String = q.shift();
var l:String = q.pop();
var q = new Queue();  // or use: new Queue<String>();
q.add('hello');
q.add('world');

var f = q.removeFirst();
var l = q.removeLast();

Associative Arrays, Maps and Dictionaries

var m1:Object = new Object();
m1.name1 = 'Susan';
m1.name2 = 'Michael';

var m2:Object = {name1 : 'Susan', name2 : 'Michael'};
delete m2.name2;

var m3:Dictionary = new Dictionary();
m3[0] = new Point(0, 0);
m3[1] = new Point(1, 1);
delete m3[1];
var m1 = new Map();   // or use: new Map<String, String>();
m1['name1'] = 'Susan';
m1['name2'] = 'Michael';
  
var m2 = {'name1' : 'Susan', 'name2' : 'Michael'};
m2.remove('name2');
  
var m3 = new Map<int, Point>();
m3[0] = new Point(0, 0);
m3[1] = new Point(1, 1);
m3.remove(1);

Sets

// A set is a collection without duplicates.
// ActionScript has no built-in support for sets.
//
//
var s = new Set();  // or use: new Set<String>();
s.add('hello');
s.add('world');
s.add('hello');     // will not be added to avoid duplicates.

Loops

for

for(var i:int = 1; i < 10; i++) {
  trace(i);
}
for(int i = 1; i < 10; i++) {
  print(i);
}

for each

var words:Array = ['this', 'is', 'sparta!'];  
for each(var word:String in words) {
  trace(word);
}
var words = ['this', 'is', 'sparta!'];
for(var word in words) {
  print(word);
}

while and do-while

var i:int = 0;
while(i < 10) { i++; }

var j:int = 0;
do { j++; } while(j < 10);
int i = 0;
while(i < 10) { i++; }

int j = 0;
do { j++; } while(j < 10);

iterate objects/maps

var data:Object = {name1 : 'Susan', name2 : 'Michael'};
for(var key:String in data) {
  var value:String = data[key];
}
var data = {'name1' : 'Susan', 'name2' : 'Michael'};
for (var key in data.keys) {
  var value = data[key];
}

Mathematics

Library

// All mathematical operations are static member fields
// and functions of the 'Math' class.
//
//
// All mathematical operations are static top level constants
// and functions of the 'dart:math' library.

import 'dart:math';

Constants

var p:Number = Math.PI;
var e:Number = Math.E;
var s:Number = Math.SQRT2;
var p = PI;
var e = E;
var s = SQRT2;

Trigonometric functions

var s:Number = Math.sin(Math.PI / 2);
var c:Number = Math.cos(Math.PI / 3);
var t:Number = Math.tan(Math.PI / 4);
var s = sin(PI / 2);
var c = cos(PI / 3);
var t = tan(PI / 4);

Random

var r:Number = Math.random();
 
var random = new Random();
var r = random.nextDouble();

Abs, Ceil, Floor and Round

var a:Number = Math.abs(-3.14);
var c:Number = Math.ceil(15.54);
var f:Number = Math.floor(27.17);
var r:Number = Math.round(35.22);
var a = (-3.14).abs();
var c = (15.54).ceil();
var f = (27.17).floor();
var r = (35.22).round();

String to Number conversion

var i:int = parseInt('12');
var n:Number = parseFloat('27.55');
var i = int.parse('12');
var n = double.parse('27.55');

Lexical Scoping

Hoisting

// ActionScript uses function scoping.

public function foo():void {
  // No warning, variable 'i' is hoisted.
  trace (i);
      
  for(var i:int = 0; i < 10; i++)
    trace(3 * i);  
  
  // Warning: Duplicate variable definition.
  for(var i:int = 0; i < 10; i++)
    trace(5 * i);  
}
// Dart uses block scoping.

void foo() {
  // Error: cannot resolve 'i'.
  print (i);
  
  for(int i = 0; i < 10; i++)
    print(3 * i); 

  // No warning, variable 'i' isn't used in this scope
  for(int i = 0; i < 10; i++)
    print(5 * i); 
}

Closures

// The following code prints:  5, 5, 5, 5, 5

var callbacks:Array = new Array();
      
for(var i:int = 0; i < 5; i++) {
  callbacks.push(function():void { trace(i); });
}

for each(var callback:Function in callbacks) {
  callback();      
}
// The following code prints:  0, 1, 2, 3, 4

var callbacks = new List();
  
for(int i = 0; i < 5; i++) {
  callbacks.add(() { print(i); });
}
  
for (var callback in callbacks) {
  callback();     
}

Scope of 'this'

// ActionScript uses a dynamic binding for 'this'.
// The function 'Bar.test' will print 'Michael'.

public class Foo {
  public var name:String = 'Susan';
  
  public function printNameFunction():Function {
    return function():void { trace(this.name); };
  }
}

public class Bar { 
  public var name:String = 'Michael';
  public var func:Function;
  
  public function test():void {   
    this.func = new Foo().printNameFunction();
    this.func();  // prints 'Michael' which is problematic!
  }  
}
// Dart uses a lexical binding for 'this'.
// The function 'Bar.test' will print 'Susan'.

class Foo {
  String name = 'Susan';
  
  Function printNameFunction() {
    return () { print(this.name); }; 
  }
}

class Bar { 
  String name = 'Michael';
  Function func;
  
  void test() {   
    this.func = new Foo().printNameFunction();
    this.func();  // prints 'Susan' which is correct!
  } 
}

Miscellaneous

Exception handling

public function foo(text:String):void {
  try {
    if (text == null)
      throw new ArgumentError("Oh no!");
  } catch(ae:ArgumentError) {
    // catch ArgumentErrors
  } catch(e:Error) {
    // catch everything else
  } finally {
    // always run this code.
  }
}
void foo(String text) {
  try {
    if (text == null)
      throw new ArgumentError("Oh no!");
  } on ArgumentError catch(ae) {
    // catch ArgumentErrors
  } catch(e) {
    // catch everything else
  } finally {
    // always run this code.
  }
}

Optional parameters

// Positional optional parameters
public function foo(s:String = null, b:Boolean = false):void {
  // Parameter 'b': ActionScript does not support optional 
  // parameters without default values. 
}


// Named optional parameters
// ActionScript does not support named optional parameters.
 
// Positional optional parameters    
void foo([String s = null, bool b]) {
  if (b == null) {
    // Parameter 'b' was not provided.
  }
}

// Named optional parameters
void bar({String s:null, bool b:false}):void {
}

Single expression functions

public function get name():String {
  return _name;
}

public function calc(value:int):int {
  return 2 * value;
}

var func:Function = function(text:String):void { 
  trace(text.toUpperCase()); 
};
String get name() => _name;



int calc(int value) => 2 * value; 
 

 
var func = (String text) => print(text.toUpperCase());
  
  

Method cascades

// this feature is not available in ActionScript, 
// therefore you write something like this:

var sprite:Sprite = new Sprite();
sprite.x = 100;
sprite.y = 150;
sprite.addEventListener(MouseEvent.CLICK, onMouseClick);
stage.addChild(sprite);
// Dart offers so called method cascades, it is
// a shorter way to write code like this:

var sprite = new Sprite()
..x = 100
..y = 150
..addEventListener(MouseEvent.CLICK, onMouseClick)
..addTo(stage);

Printing to the console

trace('hello world');
print('hello world');

String Concatenation and Interpolation

public function hello(first:String, last:String):String {
  return 'Hello ' + first + ' ' + last;
}
String hello(String first, String last) {
  return 'Hello $first $last';
}

Escaped and Raw String

var esc:String = "Hi!\\n\\nNice to meet you!\\n";
// ActionScript does not support raw Strings.
var esc = "Hi!\\n\\nNice to meet you!\\n";
var raw = r"Hi!\n\nNice to meet you!\n";

Regular Expressions

// ActionScript supports the RexExp class and literal.

var pattern1:RegExp = new RegExp("^\\d+$", "");
var pattern2:RegExp = /^\d+$/;
var match:Array = pattern1.exec("42");
trace(match[0]); // 42
// Dart only supports the RexExp class. 

var pattern1 = new RegExp(r"^\d+$");

var match = pattern1.firstMatch("42");
print(match.group(0)); // 42

External Interface

if (ExternalInterface.available) {
  ExternalInterface.call("updateHtml", 1, 2, 3);
}
updateHtml(1, 2, 3);
 
 

Libraries

StageXL (https://github.com/bp74/stagexl)

// Flash Display List objects
var bd:BitmapData = new BitmapData(20, 20, false, 0xFF00FF);
var b:Bitmap = new Bitmap(bd);
b.rotation = 45; 

var sprite:Sprite = new Sprite();
sprite.addChild(b);
sprite.scaleX = 0.25;
sprite.scaleY = 0.50;
sprite.addEventListener(MouseEvent.CLICK, onMouseClick);

// Flash audio playback
var sound:Sound = new Embedded.CoolSound();
var soundChannel:SoundChannel = s.play();
// HTML5 Display List objects
var bd = new BitmapData(20, 20, false, 0xFF00FF);
var b = new Bitmap(bd); 
b.rotation = 45 * PI / 180;

var sprite = new Sprite();
sprite.addChild(b);
sprite.scaleX = 0.25;
sprite.scaleY = 0.50;
sprite.addEventListener(MouseEvent.CLICK, onMouseClick);

// HTML5 audio playback
var sound = resource.getSound("CoolSound");
var soundChannel = s.play();

dart-xml (https://github.com/prujohn/dart-xml)

var xml:XML = 
  <persons>
    <person name='Susan'/>
    <person name='Michael'/>
  </persons>;    
      
var xmlList:XMLList = xml.elements('person');
var xml = XML.parse('''
<persons>
  <person name='Susan'/>
  <person name='Michael'/>
</persons>
''';
var xmlList = xml.queryAll('person');

dartbox2D (http://code.google.com/p/dartbox2d)

var body:b2Body;
var bodyDef:b2BodyDef;
var boxShape:b2PolygonShape;
var circleShape:b2CircleShape;
Body body;
BodyDef bodyDef;
PolygonShape boxShape;
CircleShape circleShape;

Using a library

import flash.display.*;
import com.hurlant.crypto.hash.SHA1;
  
import 'package:stagexl/stagexl.dart';
import 'dart:crypto' show SHA1; // only SHA1 class is visible
import 'dart:html' as html; // Alias, use 'html.window...'

New theme

New synonym