高見龍

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

CoffeeScript OOP篇

在JavaScript裡寫OOP有好幾種方式,例如你可以使用prototype的方式,也可以使用function或物件的方式來寫,不過其實不管哪一種,類別的程式碼寫起來、看起來都不夠直覺(當然也部份是因為我對JavaScript沒這麼熟)。還好在CoffeeScript寫類別是相當容易,而且程式碼是很清楚、容易閱讀的,來看個例子:

1
2
3
4
5
6
7
class Animal
  constructor: (name, age) ->
      this.name = name
      this.age = age

animal = new Animal("eddie", 18)
console.log animal

編譯結果:

1
2
3
4
5
6
7
8
9
10
var Animal, animal;
Animal = (function() {
  function Animal(name, age) {
      this.name = name;
      this.age = age;
  }
  return Animal;
})();
animal = new Animal("eddie", 18);
console.log(animal);

看到了嗎? 用CoffeeScript來定義類別真的簡單多了。特別注意一下constructor(建構子)的部份,這邊的:

1
this.name = name

因為在CoffeeScript裡@符號代表this,所以可以簡寫成:

1
@name = name

整個很有Ruby味道。又,因為建構子用的this.name = name這樣指定動作太常見,所以:

1
2
3
4
class Animal
  constructor: (name, age) ->
      this.name = name
      this.age = age

可以更簡化成:

1
2
class Animal
  constructor: (@name, @age) ->

方法

那如果要加其它的function到類別裡要怎麼做?

1
2
3
4
5
6
7
8
class Animal
  constructor: (@name, @age) ->

  say_hello: (something) ->
      console.log "Hello, #{something}"

animal = new Animal("eddie", 18)
animal.say_hello("CoffeeScript")

編譯結果:

1
2
3
4
5
6
7
8
9
10
11
12
13
var Animal, animal;
Animal = (function() {
  function Animal(name, age) {
      this.name = name;
      this.age = age;
  }
  Animal.prototype.say_hello = function(something) {
      return console.log("Hello, " + something);
  };
  return Animal;
})();
animal = new Animal("eddie", 18);
animal.say_hello("CoffeeScript");

編譯出來的程式碼已經有點開始長了..

繼承

在CoffeeScript裡,繼承使用extends這個關鍵字:

1
2
3
4
5
6
7
8
9
10
11
12
class Animal
  constructor: (@name, @age) ->
  say_hello: (something) ->
      console.log "Hello, #{something}"

class Human extends Animal
  walk: ->
      console.log "I can walk with my foots!"

eddie = new Human("eddie", 18)
eddie.say_hello "CoffeeScript"
eddie.walk()

編譯結果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var Animal, Human, eddie;
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, age) {
      this.name = name;
      this.age = age;
  }
  Animal.prototype.say_hello = function(something) {
      return console.log("Hello, " + something);
  };
  return Animal;
})();

Human = (function() {
  __extends(Human, Animal);
  function Human() {
      Human.__super__.constructor.apply(this, arguments);
  }
  Human.prototype.walk = function() {
      return console.log("I can walk with my foots!");
  };
  return Human;
})();
eddie = new Human("eddie", 18);
eddie.say_hello("CoffeeScript");
eddie.walk();

看到編譯出來的JavaScript程式碼了嗎? 跟CoffeeScript的程式碼行數以及複雜度比起來,我想誰比較容易寫應該一目了然了。

在OOP的繼承體系裡,子類別會自動繼承自父類別的方法,如果兒子覺得老爸的方法不好用,兒子也可以自己定義一個來用,以上面的例子來說:

1
2
3
4
class Human extends Animal
  say_hello: (something) ->
      console.log "Hi, I am #{@name}"
      super something

你可以在子類別定義同名的function,以來取代或補充父類別的方法,如果在override的方法裡要存取父類,則是使用關鍵字super

有興趣的話,大家可以自己試著編譯看看這時候的程式碼是不是已經有點看不太懂了,但CoffeeScript的部份應該看起來還算滿清楚的。

Comments