CoffeeScript 本身也是一種程式語言,開發者可以透過撰寫 CoffeeScript,編譯產生 JavaScript。它的語法有點像是 Ruby 與 Python 的混合體。
不得不坦承,當初 Rails 3.1 選擇同時納入 SCSS 以及 CoffeeScript 作為預設支援時,開發者是一半歡欣一半困惑的。歡欣的是納入 SCSS,困惑的是引入 CoffeeScript。
SCSS 讓撰寫 CSS 變得無比的直觀,並藉由 Compass 引入了不少 killer features。但使用 CoffeeScript 卻沒有這麼多壓倒的好處,有些人也形容,撰寫 CoffeeScript 需要腦袋內建 compiler …
好像用 Python 在寫 Ruby,但實際上寫的是 JavaScript
這是 CoffeeScript 的一段範例
name = "Rails";
greeting_words = "Hi, #{name}"
say_hi = (name) ->
if name
"Hello, #{name}"
else
greeting_words
編譯之後會變成
var greeting_words, name, say_hi;
name = "Rails";
greeting_words = "Hi, " + name;
say_hi = function(name) {
if (name) {
return "Hello, " + name;
} else {
return greeting_words;
}
};
greeting_words 用的是 Ruby 語法
greeting_words = "Hi, #{name}"
say_hi 內的 if / else 則使用 Python 縮排
if name
"Hello, #{name}"
else
greeting_words
這是 CoffeeScript 的另外一段範例
jQuery ->
$('#content').focus
編譯之後會變成
jQuery(function() {
return $('#content').focus;
});
乍看之下,CoffeeScript 給人一種詭異感覺:它讓寫 JavaScript 這件事變得更複雜?但也更簡單?…似乎有點矛盾。
Plurk 前創辦人 amix 曾寫過一篇這樣的 post:「CoffeeScript: The beautiful way to write Javascript」來這樣形容 CoffeeScript:「以更漂亮的方式撰寫 JavaScript」。
他認為目前 JavaScript 存在幾種問題:
而 CoffeeScript 的誕生,原因就是就是為了扭正這樣的局面,重新讓寫 JavaScript 這件事也可以變得「很美」。
CoffeeScript 做了幾項努力:
JavaScript 目前使用的是與本身型態不搭嘎的 C / Java 語法。CoffeeScript 改採用了深受 Lisp 影響的 Ruby + Python 的混合語法。
CoffeeScript 從其他語言借了不少好處,寫起來更方便。
# Array
list = [1, 2, 3, 4, 5]
# list comprehensions
cubes = (math.cube num for num in list)
# array slicing
copy = list[0...list.length]
# array generators
countdown = (num for num in [10..1])
author = "Wittgenstein"
quote = "A picture is a fact. -- #{ author }"
生成
var author, quote;
author = "Wittgenstein";
quote = "A picture is a fact. -- " + author;
多行 String 也沒問題
mobyDick = "Call me Ishmael. Some years ago -
never mind how long precisely -- having little
or no money in my purse, and nothing particular..."
這些例子若是直接使用 JavaScript 實作,肯定痛苦個半死…
CoffeeScript 留下了 JavaScript 的 Good Parts,而在設計上極力消除 JavaScript 原生特性會產生的缺點,例如:
開發者在寫 JavaScript 時,常不自覺的使用全域變數,導致很多污染問題。而透過 CoffeeScript 生出來的 JavaScript,變數一律為區域變數( 以 var 開頭)
使用 CoffeeScript 撰寫 function,產生出來的 JavaScript 必以一個 anonymous function: function(){}(); 自我包裹,獨立運作不干擾到其他 function。
在撰寫 JavaScript 時,最令人不爽的莫過於 function(){}();,這些複雜的括號和分號稍微一漏,程式就不知道死在哪裡了….
CoffeeScript 自動產生出來的 JavaScript 能夠確保括號們絕對不會被漏掉。
JavaScript 在 syntax error 時,非常難以偵測錯誤,幾乎是每個程式設計師的夢靨。而 CoffeeScript 是一門需要 compile 的語言,可以藉由這樣的特性擋掉 syntax error 的機會。
Javascript 是一種物件導向語言,裡面所有東西幾乎都是物件。但 JavaScript 又不是一種真正的物件導向語言,因為它的語法裡面沒有 class(類別)。
在 JavaScript 中,我們要實作 OOP 有很多種方式,你可以使用 prototype、function 或者是 Object。但無論是哪一種途徑,其實都「不簡單」。
但 CoffeeScript 讓這件事變簡單了,比如以官網的這個例子:
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()
class Animal
constructor: (@name) ->
自動生成
var Animal;
Animal = (function() {
function Animal(name) {
this.name = name;
}
return Animal;
})();
class Animal
constructor: (@name) ->
move: (meters) ->
alert @name + " moved #{meters}m."
自動生成
var Animal;
Animal = (function() {
function Animal(name) {
this.name = name;
}
Animal.prototype.move = function(meters) {
return alert(this.name + (" moved " + meters + "m."));
};
return Animal;
})();
class Animal
constructor: (@name) ->
move: (meters) ->
alert @name + " moved #{meters}m."
class Snake extends Animal
move: ->
alert "Slithering..."
super 5
自動生成
var Animal, Snake;
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;
})();
因為實作物件導向非常容易,所以撰寫測試這件事也更容易了。所以不少 Node.js 的套件或者是 JavaScript 的 Library 後來都改以 CoffeeScript 去實作。