高見龍

iOS app/Ruby/Rails Developer & Instructor, 喜愛非主流的新玩具 :)

CoffeeScript 基本篇(一)

首先,CoffeeScript的官網的文件寫得相當的詳細,強烈建議各位一定要看,只要看完一輪,大概就知道CoffeeScript是怎麼一回事了。

底下你看到的console.log,通常是拿來debug用的,並不會真的在網頁上產生結果,請搭配Firebug使用。

註解

CoffeeScript跟Ruby一樣,使用#字號來做註解,而且註解的內容在編譯過的js檔裡是看不到的。如果要做多行註解,則是使用3個#字號。

變數宣告

其實變數不用特別宣告,直接抓來用就好了,例如:

1
2
age = 18;
name = "CoffeeScript";

編譯結果:

1
2
3
var age, name;
age = 18;
name = "CoffeeScript";

看到了嗎? 它幫你宣告好了agename兩個變數,而且該加的分號都加上去了。

如果你要組合字串,除了用加號來串接外,CoffeeScript也從Ruby那邊借來了string interpolation:

1
greeting = "hello, #{name}"

編譯結果:

1
greeting = "hello, " + name;

要注意的是,如果你用的是單引號的話,string interpolation不會發生作用。

跟Ruby一樣,string interpolation可以放一個完整的expression,例如:

1
2
3
4
5
6
greeting = "hello, #{
  if name
      name
  else
      "CoffeeScript"
}"

編譯結果:

1
2
var greeting;
greeting = "hello, " + (name ? name : "CoffeeScript");

Function

1
2
3
4
say_hello = ->
  "Hello CoffeeScript"

say_hello()

編譯結果:

1
2
3
4
5
var say_hello;
say_hello = function() {
  return "Hello CoffeeScript";
};
say_hello();

像Ruby一樣,function的最後一個執行結果自動就是整個function的回傳值,所以這邊即使沒有特別寫return,該加上的return、大括號以及分號,都在編譯成js檔之後加上去了。(你要特別加上return也ok,不會變兩個return的)

再來,如果你要給function傳進參數的話:

1
2
3
4
5
6
7
8
9
10
11
12
age = 18;
name = "CoffeeScript";
greeting = "hello, #{name}"
say_hello = (name) ->
if name
  "hello, #{name}"
else
  greeting

console.log say_hello()
console.log say_hello("eddie")
console.log say_hello "eddie"

編譯結果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var age, greeting, name, say_hello;
age = 18;
name = "CoffeeScript";
greeting = "hello, " + name;
say_hello = function(name) {
  if (name) {
      return "hello, " + name;
  } else {
      return greeting;
  }
};
console.log(say_hello());
console.log(say_hello("eddie"));
console.log(say_hello("eddie"));

這樣就可以傳參數進去了。

上面這段範例有兩件事要特別留意,首先,CoffeeScript像Python一樣使用縮排(identation)做為程式碼區塊的判斷,所以上面if..else..的縮排是有意義的。是的,if後面不用加then

第二,注意到在上面傳參數的時候,像Ruby一樣的省略掉了小括號嗎? 編譯出來的結果跟有小括號的是一樣的。那為什麼say_hello不傳參數的時候不能像Ruby一樣省略參數? 我想是因為CoffeeScript沒辦法真的像Ruby一樣辨識say_hello到底是一般的變數,還是沒有參數的方法呼叫,所以請記得,如果沒傳參數給function,請記得加上小括號

如果你要讓參數有預設值:

1
2
say_hello = (name = "CoffeeScript") ->
  console.log "Hello, #{name}"

編譯結果:

1
2
3
4
5
6
7
var say_hello;
say_hello = function(name) {
  if (name == null) {
      name = "CoffeeScript";
  }
  return console.log("Hello, " + name);
};

在Ruby裡傳參數,有叫做Splat的概念,可以一次傳進多個選擇性的參數,在CoffeeScript也有借來用,只是寫法有點不太一樣:

1
2
3
4
5
6
7
8
9
say_hello = (name, others...) ->
  if others.length > 0
      "Hello, #{name} and #{others}"
  else
      "Hello, #{name}"

console.log say_hello "eddie"
console.log say_hello "eddie", "kao"
console.log say_hello "eddie", "kao", "joanne"

編譯結果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var say_hello;
var __slice = Array.prototype.slice;
say_hello = function() {
  var name, others;
  name = arguments[0], others = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
  if (others.length > 0) {
      return "Hello, " + name + " and " + others;
  } else {
      return "Hello, " + name;
  }
};

console.log(say_hello("eddie"));
console.log(say_hello("eddie", "kao"));
console.log(say_hello("eddie", "kao", "joanne"));

第二個參數others後面接三個點,表示它是一個「可有可無而且可以不定數量」的參數,而傳進來的參數變成一個陣列,看到編譯出來的程式碼就覺得如果要自己手工寫出這段程式碼要想好一下子的..

像Ruby一樣,CoffeeScript支援變數多重指定,但寫法有些不一樣,需要特別加上中括號,所以可以這樣玩:

1
2
3
4
[first, others..., last] = [1..10]
console.log first
console.log last
console.log others

編譯結果:

1
2
3
4
5
6
var first, last, others, _i, _ref;
var __slice = Array.prototype.slice;
_ref = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], first = _ref[0], others = 3 <= _ref.length ? __slice.call(_ref, 1, _i = _ref.length - 1) : (_i = 1, []), last = _ref[_i++];
console.log(first);     # 1
console.log(last);      # 10
console.log(others);    # [ 2, 3, 4, 5, 6, 7, 8, 9 ]

變數可以多重指派,當然也可以交換:

1
2
3
4
5
6
x = 1
y = 10
[x, y] = [y, x]

console.log x
console.log y

編譯結果:

1
2
3
4
5
6
var x, y, _ref;
x = 1;
y = 10;
_ref = [y, x], x = _ref[0], y = _ref[1];
console.log(x);
console.log(y);

可以用原來的方式寫function嗎?

你不一定要照著CoffeeScript的方式寫,如果CoffeeScript的語法讓你覺得不舒服,你希望可以用你習慣的JavaScript語法的話也ok,你可以這樣做:

1
2
3
say_hello = `function(name){
  return "Hello, " + name
}`

在反單引號(backtick)包起來的內容,在編譯的時候會照你寫的內容輸出,只是這樣一來用CoffeeSCript就沒意義啦!

陣列

1
heroes = ['Spider Man', 'Captain America', 'X-men', 'Iron Man']

在CoffeeScript裡,你可以用多行表示:

1
2
3
4
5
6
heroes = [
  'Spider Man',
  'Captain America',
  'X-men',
  'Iron Man'
]

編譯結果:

1
2
var heroes;
heroes = ['Spider Man', 'Captain America', 'X-men', 'Iron Man'];

甚至連每行後面的逗點也都可以省下來。

CoffeeScript從Ruby借來了Range

1
students = [1..10]

編譯結果:

1
2
var students;
students = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

如果是多一個點:

1
students = [1...10]

編譯結果:

1
2
var students;
students = [1, 2, 3, 4, 5, 6, 7, 8, 9];

如果你把Range反過來寫,例如[10..1],它就會變成從10到1的數字陣列。但如果把Range的數字放大一點:

1
students = [1..100]

編譯結果:

1
2
3
4
5
6
var students, _i, _results;
students = (function() {
  _results = [];
  for (_i = 1; _i <= 100; _i++){ _results.push(_i); }
  return _results;
}).apply(this, arguments);

只要Range大於20就會改用迴圈的方式來建構這個陣列。

陣列的操作也是很容易的

1
heroes[0..2]

編譯結果:

1
heroes.slice(0, 3);

如果利用Range來替換或新增陣列的內容:

1
heroes[1..2] = ["Batman", "ThunderCat"]

編譯結果:

1
2
3
var _ref;
heroes = ['Spider Man', 'Captain America', 'X-men', 'Iron Man'];
[].splice.apply(heroes, [1, 2].concat(_ref = ["Batman", "ThunderCat"])), _ref;

物件

你可以用這種JSON的寫法:

1
eddie = { name: "Eddie Kao", age: 18, speciality: "eat" }

或是YAML式的寫法:

1
2
3
4
eddie =
  name: "Eddie Kao"
  age: 18
  speciality: "eat"

編譯結果:

1
2
3
4
5
6
var eddie;
eddie = {
  name: "Eddie Kao",
  age: 18,
  speciality: "eat"
};

特別是如果你要做巢狀的物件結構,用YAML的寫法會更清楚:

1
2
3
4
5
6
7
8
9
10
11
eddie =
  name: "Eddie Kao"
  age: 18
  speciality: "eat"
  lovers:
  nayumi:
      name: "Nayumi Hung"
      age: 18
  mary:
      name: "Mary Bloody"
      age: 20

Comments