(ε・◇・)з CoffeeScript と mofmof.js
(ε・◇・)з CoffeeScript は Ruby や Python っぽく書けて JavaScript にコンパイルできるプリティな言語だとか!
(ε・◇・)з 噂では CoffeeScript で書くとコードが短くなるとか!!
物は試しに http://jashkenas.github.com/coffee-script/ にある幾つかのコード片と、mofmof.js 混じりで書いた場合のコードを比べてみました。
(ε・◇・)з mofmof.js はここだよ → http://code.google.com/p/mofmof-js/w/list
Syntax
// coffee # Assignment: number = 42 opposite = true # Conditions: number = -42 if opposite # Functions: square = (x) -> x * x # Arrays: list = [1, 2, 3, 4, 5] # Objects: math = root: Math.sqrt square: square cube: (x) -> x * square x # Splats: race = (winner, runners...) -> print winner, runners # Existence: alert "I knew it!" if elvis? # Array comprehensions: cubes = (math.cube num for num in list) // coffee -> js ---------------------------- var cubes, list, math, num, number, opposite, race, square; var __slice = Array.prototype.slice; number = 42; opposite = true; if (opposite) number = -42; square = function(x) { return x * x; }; list = [1, 2, 3, 4, 5]; math = { root: Math.sqrt, square: square, cube: function(x) { return x * square(x); } }; race = function() { var runners, winner; winner = arguments[0], runners = 2 <= arguments.length ? __slice.call(arguments, 1) : []; return print(winner, runners); }; if (typeof elvis !== "undefined" && elvis !== null) alert("I knew it!"); cubes = (function() { var _i, _len, _results; _results = []; for (_i = 0, _len = list.length; _i < _len; _i++) { num = list[_i]; _results.push(math.cube(num)); } return _results; })(); // mofmof ---------------------------- // Assignment: var number = 42, opposite = true; // Conditions: opposite && (number = -42); // Functions: function square(x) { return x * x; } // Arrays: list = [1, 2, 3, 4, 5]; // Objects: math = { root: Math.sqrt, square: square, cube: function(x) { return x * square(x); } }; // Splats: function cube(winner, runners, /*... */) { return print.apply(null, arguments); } // Existence: elvis != null && alert("I knew it!"); // Array comprehensions: cubes = list.filter(math.cube);
(ε・◇・)з 基本 Syntax だと、あまり変わった感じしませんね〜
Functions
// coffee square = (x) -> x * x cube = (x) -> square(x) * x fill = (container, liquid = "coffee") -> "Filling the #{container} with #{liquid}..." // coffee -> js ---------------------------- var cube, square; square = function(x) { return x * x; }; cube = function(x) { return square(x) * x; }; var fill; fill = function(container, liquid) { if (liquid == null) liquid = "coffee"; return "Filling the " + container + " with " + liquid + "..."; }; // mofmof.js ---------------------------- function square(x) { return x * x; }; function cube(x) { return square(x) * x; } function fill(container, liquid /* = coffee */) { return "Filling the @@ with @@...".f(container, liquid || "coffee"); }
(ε・◇・)з Coffee は function が -> になってる分だけタイプ数少なめですね
Loops and Comprehensions
// coffee # Eat lunch. eat food for food in ['toast', 'cheese', 'wine'] // coffee -> js ---------------------------- var food, _i, _len, _ref; _ref = ['toast', 'cheese', 'wine']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { food = _ref[_i]; eat(food); } // mofmof ---------------------------- ['toast', 'cheese', 'wine'].forEach(eat); or var iter = Hash(['toast', 'cheese', 'wine']), food; while (food = iter.next()) { eat(food); }
(ε・◇・)з mofmof.js はforEachやイテレータ使ってます。実行速度が重要でなければ、このへんは好みかな〜
// coffee countdown = (num for num in [10..1]) // coffee -> js ---------------------------- var countdown, num; countdown = (function() { var _results; _results = []; for (num = 10; num >= 1; num--) { _results.push(num); } return _results; })(); // mofmof ---------------------------- countdown = 10..$(1);
(ε・◇・)з mofmof.js は Number.prototype.$ を拡張して 10..$(1) で 10 から1までの配列を生成していますよ〜
// coffee yearsOld = max: 10, ida: 9, tim: 11 ages = for child, age of yearsOld "#{child} is #{age}" // coffee -> js ---------------------------- var age, ages, child, yearsOld; yearsOld = { max: 10, ida: 9, tim: 11 }; ages = (function() { var _results; _results = []; for (child in yearsOld) { age = yearsOld[child]; _results.push("" + child + " is " + age); } return _results; })(); // mofmof ---------------------------- var yearsOld = { max: 10, ida: 9, tim: 11 }; Hash.map(yearsOld, function(age, child) { return "@@ is @@".f(child, age); });
(ε・◇・)з Coffee側のコードはシンプルですね〜
Array Slicing and Splicing with Ranges
// coffee numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] copy = numbers[0...numbers.length] middle = copy[3..6] // coffee -> js ---------------------------- var copy, middle, numbers; numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; copy = numbers.slice(0, numbers.length); middle = copy.slice(3, 7); // mofmof ---------------------------- var numbers = 0..$(9); var copy = numbers.clip(0, numbers.length); var middle = copy.clip(3, 6);
(ε・◇・)з もふもふ!
// coffee numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] numbers[3..6] = [-3, -4, -5, -6] // coffee -> js ---------------------------- var numbers, _ref; numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; [].splice.apply(numbers, [3, 4].concat(_ref = [-3, -4, -5, -6])), _ref; // mofmof ---------------------------- numbers = 0..$(9); numbers.swap(3, (-3).$(-6));
(ε・◇・)з mofmof 側は、Array#swap で配列の一部をごっそり入れ替えてます〜
Everything is an Expression (at least, as much as possible)
// coffee globals = (name for name of window)[0...10] // coffee -> js ---------------------------- var globals, name; globals = ((function() { var _results; _results = []; for (name in window) { _results.push(name); } return _results; })()).slice(0, 10); // 全部とってきて10個だけ返す // mofmof ---------------------------- globals = Hash.keys(window, 10); // 10個とってきて返す
(ε・◇・)з mofmof の Hash.keys は、第二引数で列挙する要素の最大数を指定できるので、10個だけ列挙して返します〜
Classes, Inheritance, and Super
// coffee class Animal constructor: (@name) -> move: (meters) -> alert @name + " moved #{meters}m." class Snake extends Animal move: -> alert "Slithering..." super 5 class Horse extends Animal move: -> alert "Galloping..." super 45 sam = new Snake "Sammy the Python" tom = new Horse "Tommy the Palomino" sam.move() tom.move() // coffee -> js ---------------------------- var Animal, Horse, Snake, sam, tom; var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; }; Animal = (function() { function Animal(name) { this.name = name; } Animal.prototype.move = function(meters) { return alert(this.name + (" moved " + meters + "m.")); }; return Animal; })(); Snake = (function() { __extends(Snake, Animal); function Snake() { Snake.__super__.constructor.apply(this, arguments); } Snake.prototype.move = function() { alert("Slithering..."); return Snake.__super__.move.call(this, 5); }; return Snake; })(); Horse = (function() { __extends(Horse, Animal); function Horse() { Horse.__super__.constructor.apply(this, arguments); } Horse.prototype.move = function() { alert("Galloping..."); return Horse.__super__.move.call(this, 45); }; return Horse; })(); sam = new Snake("Sammy the Python"); tom = new Horse("Tommy the Palomino"); sam.move(); tom.move(); // mofmof ---------------------------- mm.Class("Animal", { init: function(name) { this.name = name; }, move: function(meters) { alert("@@ moved @@m.".f(this.name, meters)); } }); mm.Class("Snake:Animal", { move: function() { alert("Slithering..."); this.superCall("move", 5); } }); mm.Class("Horse:Animal", { move: function() { alert("Galloping..."); this.superCall("move", 45); } }); var sam = mm("Snake", "Sammy the Python"); var tom = mm("Horse", "Tommy the Palomino"); sam.move(); tom.move();
(ε・◇・)з mofmof にもクラスあるんだよ〜
(ε・◇・)з 最大継承数は1段に限定してある(昔は3段までサポートしてた)けど、1段継承できればニーズの9割カバーできるからOKなんだよ〜 (それ以上継承したい人は変態的なコードを書きたい人だけだと思うよ〜
Destructuring Assignment
// coffee theBait = 1000 theSwitch = 0 [theBait, theSwitch] = [theSwitch, theBait] // coffee -> js ---------------------------- var theBait, theSwitch, _ref; theBait = 1000; theSwitch = 0; _ref = [theSwitch, theBait], theBait = _ref[0], theSwitch = _ref[1]; // mofmof ---------------------------- var theBait = 1000, theSwitch = 0; [theBait, theSwitch].swap(0, [theSwitch, theBait]);
(ε・◇・)з ここでも Array#swap が出てきてます〜
Chained Comparisons
// coffee cholesterol = 127 healthy = 200 > cholesterol > 60 // coffee -> js ---------------------------- var cholesterol, healthy; cholesterol = 127; healthy = (200 > cholesterol && cholesterol > 60); // mofmof ---------------------------- var cholesterol = 127 healthy = cholesterol.inRange(200, 60);
(ε・◇・)з Coffee のほうが分かりやすいね〜
おしまい
(ε・◇・)з mofmof.js も結構イケてるんじゃないでしょうか!