본문 바로가기
프로그래밍 놀이터/Web

[Script] CoffeeScript Tutorial & Sample Codes ( 기초강좌 & 샘플코드 )

by 돼지왕 왕돼지 2012. 9. 22.
반응형





Coffee Script 소개

- CoffeeScript 는 컴파일하면 JavaScript 가 되는 작은 규모의 언어.

- JavaScript의 라이브러리를 가져다 쓸 수도 있다.

- 컴파일시에는 JavaScript Lint 를 통해 컴파일되기 때문에 안정성 측면에서 좋다.

- CoffeeScript 로 작업하면 보통 자바스크립트 코드보다 빠를수도 있다. ( 최적화 코드로 치환되며 실수가 적어져서 )




설치와 사용

- CoffeeScript 의 컴파일러 "coffee" 는 Node.js 의 유틸로서 사용할 수 있다. 하지만 사실 CoffeeScript 자체가 Node.js 에 의존하고 있는 것은 아니다. 

> coffee -c test.coffee

- test.coffee 라는 커피스크립트가 컴파일되어 .js 로 변환된다.


> coffee

- coffee 만 수행하면 간단한 커피스크립트 -> 자바스크립트 코드를 수행할 수 있는 커맨드 라인이 나온다. 여기서 탈출은 Ctrl + D. 웹상에서도 간단한 코드를 치환할 수 있다. http://coffeescript.org/





언어적 특성

- 커피스크립트는 스페이스바 공백을 block 구분하는 데 쓴다.

- 세미콜론( ; ) 을 사용할 필요는 없다.

- { } 대신 들여쓰기로 그 기능을 대신한다. 

- 파라미터를 전달하는 function call시 () 도 필요없다. 공백 한칸을 주고 파라미터를 주면 바로 () 의 역할을 한다. ( 파라미터가 없는 경우에는 함수임을 표시하기 위해 () 가 필요하다. 일관성을 위해서 항상 () 써주는 것도 한 방법이다. )




Function

<Function 정의 Syntax>

변수 = (파라미터, 파라미터...) -> 내용물



<함수 기본>

square = (x) -> x * x

cube   = (x) -> square(x) * x

var cube, square;


square = function(x) {

  return x * x;

};


cube = function(x) {

  return square(x) * x;

};



<파라미터 기본값 설정>

fill = (container, liquid = "coffee") ->

  "Filling the #{container} with #{liquid}..."

var fill;


fill = function(container, liquid) {

  if (liquid == null) {

    liquid = "coffee";

  }

  return "Filling the " + container + " with " + liquid + "...";

};

cf) string 안에서 script variable 에 접근하기 위해서는 #{id} 를 사용한다.




Objects and Arrays

- 기본 스크립트와 매우 비슷하다.

- comma 로 인한 구분은 option.

- reserved key 를 string 화 할 필요 없이 사용할 수 있다. coffee script 가 자동으로 이런 녀석들을 quotation 처리한다.


<Object & Array 기본>

song = ["do", "re", "mi", "fa", "so"]


singers = {Jagger: "Rock", Elvis: "Roll"}


bitlist = [

  1, 0, 1

  0, 0, 1

  1, 1, 0

]


kids =

  brother:

    name: "Max"

    age:  11

  sister:

    name: "Ida"

    age:  9

↓ 

var bitlist, kids, singers, song;


song = ["do", "re", "mi", "fa", "so"];


singers = {

  Jagger: "Rock",

  Elvis: "Roll"

};


bitlist = [1, 0, 1, 0, 0, 1, 1, 1, 0];


kids = {

  brother: {

    name: "Max",

    age: 11

  },

  sister: {

    name: "Ida",

    age: 9

  }

};



<Reserved Key Case>

$('.account').attr class: 'active'
 

log object.class

↓  

$('.account').attr({

  "class": 'active'

});


log(object["class"]);

cf) html element 속성 접근할 때는 $('id') 로 접근한다.




Lexical Scoping and Variable Safety

- 명시적으로 var 를 사용할 수 없다.

- -> 는 함수정의라는 의미.

- 변수는 무조건 최상단에 정의된다. 같은 이름을 inner & outer function 에서 사용할 경우 scope 에 주의해야 한다.

- 모든 CoffeeScript 의 output 은 (function(){…}(); 의 형태로 wrap 되어 나온다. 

- 만약 global 로 만들고 싶다면, window 에 정의하거나, exports 를 이용하여 export 시켜준다. ( exports ? this 로 접근 )

outer = 1

changeNumbers = ->

  inner = -1

  outer = 10

inner = changeNumbers()

↓   

var changeNumbers, inner, outer;


outer = 1;


changeNumbers = function() {

  var inner;

  inner = -1;

  return outer = 10;

};


inner = changeNumbers();




If, Else, Unless, and Conditional Assignment

- 조건문류는 {} 없이 들여쓰기로 구분된다.

- if 나 unless 등은 구문 앞에 올수도, 뒤에 올 수도 있다.

mood = greatlyImproved if singing


if happy and knowsIt

  clapsHands()

  chaChaCha()

else

  showIt()


date = if friday then sue else jill

  ↓    

var date, mood;


if (singing) {

  mood = greatlyImproved;

}


if (happy && knowsIt) {

  clapsHands();

  chaChaCha();

} else {

  showIt();

}


date = friday ? sue : jill

 




Splats.

- … 을 이용하여 임의적으로 추가되는 variable 들을 받을 수 있다.

gold = silver = rest = "unknown"


awardMedals = (first, second, others...) ->

  gold   = first

  silver = second

  rest   = others


contenders = [

  "Michael Phelps"

  "Liu Xiang"

  "Yao Ming"

  "Allyson Felix"

  "Shawn Johnson"

  "Roman Sebrle"

  "Guo Jingjing"

  "Tyson Gay"

  "Asafa Powell"

  "Usain Bolt"

]


awardMedals contenders... // parameter 전달시에도 ... 이 필요하다. argument 그대로 채우지 않을 경우는.


alert "Gold: " + gold

alert "Silver: " + silver

alert "The Field: " + rest

  ↓     

var awardMedals, contenders, gold, rest, silver,

  __slice = [].slice;


gold = silver = rest = "unknown";


awardMedals = function() {

  var first, others, second;

  first = arguments[0], second = arguments[1], others = 3 <= arguments.length ? __slice.call(arguments, 2) : [];

  gold = first;

  silver = second;

  return rest = others;

};


contenders = ["Michael Phelps", "Liu Xiang", "Yao Ming", "Allyson Felix", "Shawn Johnson", "Roman Sebrle", "Guo Jingjing", "Tyson Gay", "Asafa Powell", "Usain Bolt"];


awardMedals.apply(null, contenders);


alert("Gold: " + gold);


alert("Silver: " + silver);


alert("The Field: " + rest);






Loops and Comprehensions

- 일반 구문은 가장 최후의 결과를 return 한다.

- [0..10] 과 같은 구문으로 범위설정도 가능하다. 

- 범위설정시 step 을 by 로 설정 가능하다.

- 값 가져오는 것은 #{variable} 로.

- of 를 이용하여 json 에 접근.

- while 은 low-level 로 제공.

- until 은 while not 과 같다.

- do는 argument 를 전달하여 바로 function 실행.
 

 <일반 loop & for in 문>

# Eat lunch.

eat food for food in ['toast', 'cheese', 'wine']


# Fine five course dining.

courses = ['greens', 'caviar', 'truffles', 'roast', 'cake']

menu i + 1, dish for dish, i in courses
// 햇갈리는 구문. 문법처럼 외우면 되겠다. in 전에 2개의 param 이 오면 첫번째가 assign, 두번째가 index


# Health conscious meal.

foods = ['broccoli', 'spinach', 'chocolate']
eat food for food in foods when food isnt 'chocolate' 

    ↓    

var courses, dish, food, foods, i, _i, _j, _k, _len, _len1, _len2, _ref;


_ref = ['toast', 'cheese', 'wine'];

for (_i = 0, _len = _ref.length; _i < _len; _i++) {

  food = _ref[_i];

  eat(food);

}


courses = ['greens', 'caviar', 'truffles', 'roast', 'cake'];


for (i = _j = 0, _len1 = courses.length; _j < _len1; i = ++_j) {

  dish = courses[i];

  menu(i + 1, dish);

}


foods = ['broccoli', 'spinach', 'chocolate'];


for (_k = 0, _len2 = foods.length; _k < _len2; _k++) {

  food = foods[_k];

  if (food !== 'chocolate') {

    eat(food);

  }

}

 

<..을 통한 범위지정>

countdown = (num for num in [10..1])

 ↓  

var countdown, num;


countdown = (function() {

  var _i, _results;

  _results = [];

  for (num = _i = 10; _i >= 1; num = --_i) {

    _results.push(num);

  }

  return _results;

})();

 

<of 문>

yearsOld = max: 10, ida: 9, tim: 11


ages = for child, age of yearsOld

  "#{child} is #{age}"

// of 전에 parameter 는 key 가 되고 of 의 내용물을 value 가 되는 형태

 ↓   

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;

})();



<while & until 문>

# Econ 101

if this.studyingEconomics

  buy()  while supply > demand

  sell() until supply > demand

# Nursery Rhyme

num = 6

lyrics = while num -= 1

  "#{num} little monkeys, jumping on the bed.

    One fell out and bumped his head." 

 ↓ 

var lyrics, num;


if (this.studyingEconomics) {

  while (supply > demand) {

    buy();

  }

  while (!(supply > demand)) {

    sell();

  }

}


num = 6;


lyrics = (function() {

  var _results;

  _results = [];

  while (num -= 1) {

    _results.push("" + num + " little monkeys, jumping on the bed.    One fell out and bumped his head.");

  }

  return _results;

})();



< do 문 >

for filename in list

  do (filename) ->

    fs.readFile filename, (err, contents) ->

      compile filename, contents.toString()

// do 다음에 오는 function에 대해 param을 looping 돌려 적용하는데 좋다. 

 ↓  

var filename, _fn, _i, _len;


_fn = function(filename) {

  return fs.readFile(filename, function(err, contents) {

    return compile(filename, contents.toString());

  });

};

for (_i = 0, _len = list.length; _i < _len; _i++) {

  filename = list[_i];

  _fn(filename);

}





Array Slicing and Splicing with Ranges

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]


start   = numbers[0..2]


middle  = numbers[3...6]


end     = numbers[6..]


copy    = numbers[..]

 ↓   

var copy, end, middle, numbers, start;


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


start = numbers.slice(0, 3);


middle = numbers.slice(3, 6);


end = numbers.slice(6);


copy = numbers.slice(0);




numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


numbers[3..6] = [-3, -4, -5, -6]

 ↓ 

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]));





Everything is an Expression( at least, as much as possible )

- Coffee Script 의 모든 function 은 return 이 있다.

- 의도적으로 return 을 사용할 수도 있다.

- try catch 문도 모두 return 한다. ( try… catch error … )

<function return>

grade = (student) ->

  if student.excellentWork

    "A+"

  else if student.okayStuff

    if student.triedHard then "B" else "B-"

  else

    "C"


eldest = if 24 > 21 then "Liz" else "Ike"

 ↓  

var eldest, grade;


grade = function(student) {

  if (student.excellentWork) {

    return "A+";

  } else if (student.okayStuff) {

    if (student.triedHard) {

      return "B";

    } else {

      return "B-";

    }

  } else {

    return "C";

  }

};


eldest = 24 > 21 ? "Liz" : "Ike";


<variable assignment>

six = (one = 1) + (two = 2) + (three = 3)

↓ 

var one, six, three, two;


six = (one = 1) + (two = 2) + (three = 3);



<range>

# The first ten global properties.


globals = (name for name of window)[0...10]

↓  

var globals, name;


globals = ((function() {

  var _results;

  _results = [];

  for (name in window) {

    _results.push(name);

  }

  return _results;

})()).slice(0, 10);


<try - catch 문>

alert(

  try

    nonexistent / undefined

  catch error

    "And the error is ... #{error}"

)

↓  

alert((function() {

  try {

    return nonexistent / void 0;

  } catch (error) {

    return "And the error is ... " + error;

  }

})());




Operators and Aliases

CoffeeScriptJavaScript
is ===
isnt !==
not !
and &&
or ||
true, yes, on true
false, no, off false
@, this this
of in
in no JS equivalent



launch() if ignition is on


volume = 10 if band isnt SpinalTap


letTheWildRumpusBegin() unless answer is no


if car.speed < limit then accelerate()


winner = yes if pick in [47, 92, 13]


print inspect "My name is #{@name}"

↓   

var volume, winner;


if (ignition === true) {

  launch();

}


if (band !== SpinalTap) {

  volume = 10;

}


if (answer !== false) {

  letTheWildRumpusBegin();

}


if (car.speed < limit) {

  accelerate();

}


if (pick === 47 || pick === 92 || pick === 13) {

  winner = true;

}


print(inspect("My name is " + this.name));




The Existential Operator

- existence 는 ? operator 로 check 가능하다. zero, empty string, false, null, undefined 모두를 검사한다. 

- ?.는 chain 일 때 사용한다.

- if val? 은 variable 이 null 이 아닐 경우를 의미.

- ?= 는 null 이면 대입하라는 의미 

- result = val1? val2  는 val1이 null이 아니면 val1 이고 아니면 val2 라는 의미.
 

<? 문의사용>

solipsism = true if mind? and not world?


speed = 0

speed ?= 15


footprints = yeti ? "bear"

↓ 

var footprints, solipsism, speed;


if ((typeof mind !== "undefined" && mind !== null) && !(typeof world !== "undefined" && world !== null)) {

  solipsism = true;

}


speed = 0;


if (speed == null) {

  speed = 15;

}


footprints = typeof yeti !== "undefined" && yeti !== null ? yeti : "bear";



< ?문의 chain 사용>

zip = lottery.drawWinner?().address?.zipcode

↓ 

var zip, _ref;


zip = typeof lottery.drawWinner === "function" ? (_ref = lottery.drawWinner().address) != null ? _ref.zipcode : void 0 : void 0;




Classes, Inheritance, and Super

- Coffee Script 에서는 class 의 개념 있음. super 도 지정할 수 있고, constructor 를 지정할 수도 있다.

- :: 로는 object의 prototype 에 바로 접근할 수 있다.

- Class definition 에서의 this 는 class object 자신이기 때문에, static property 는 @property: value 를 통해 접근할 수 있다. 


<class definition>

class Animal

  constructor: (@name) ->


// constructor 의 (@name)-> 단순 값 assign. 즉 this.name = 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()

↓ 

var Animal, Horse, Snake, sam, tom,

  __hasProp = {}.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(_super) {


  __extends(Snake, _super);


  function Snake() {

    return Snake.__super__.constructor.apply(this, arguments);

  }


  Snake.prototype.move = function() {

    alert("Slithering...");

    return Snake.__super__.move.call(this, 5);

  };


  return Snake;


})(Animal);


Horse = (function(_super) {


  __extends(Horse, _super);


  function Horse() {

    return Horse.__super__.constructor.apply(this, arguments);

  }


  Horse.prototype.move = function() {

    alert("Galloping...");

    return Horse.__super__.move.call(this, 45);

  };


  return Horse;


})(Animal);


sam = new Snake("Sammy the Python");


tom = new Horse("Tommy the Palomino");


sam.move();


tom.move();



<Prototype access>

String::dasherize = ->

  this.replace /_/g, "-"

↓  

String.prototype.dasherize = function() {

  return this.replace(/_/g, "-");

};







Destructuring Assignment.

<Switching assignment>

theBait   = 1000

theSwitch = 0


[theBait, theSwitch] = [theSwitch, theBait]

↓  

var theBait, theSwitch, _ref;


theBait = 1000;


theSwitch = 0;


_ref = [theSwitch, theBait], theBait = _ref[0], theSwitch = _ref[1];



<Array Assignment>

weatherReport = (location) ->

  # Make an Ajax request to fetch the weather...

  [location, 72, "Mostly Sunny"]


[city, temp, forecast] = weatherReport "Berkeley, CA"

↓  

var city, forecast, temp, weatherReport, _ref;


weatherReport = function(location) {

  return [location, 72, "Mostly Sunny"];

};


_ref = weatherReport("Berkeley, CA"), city = _ref[0], temp = _ref[1], forecast = _ref[2];



<Structural assignment>

futurists =

  sculptor: "Umberto Boccioni"

  painter:  "Vladimir Burliuk"

  poet:

    name:   "F.T. Marinetti"

    address: [

      "Via Roma 42R"

      "Bellagio, Italy 22021"

    ]


{poet: {name, address: [street, city]}} = futurists

↓  

var city, futurists, name, street, _ref, _ref1;


futurists = {

  sculptor: "Umberto Boccioni",

  painter: "Vladimir Burliuk",

  poet: {

    name: "F.T. Marinetti",

    address: ["Via Roma 42R", "Bellagio, Italy 22021"]

  }

};


_ref = futurists.poet, name = _ref.name, (_ref1 = _ref.address, street = _ref1[0], city = _ref1[1]);



<Splates assignment>

tag = "<impossible>"


[open, contents..., close] = tag.split("")


↓  

var close, contents, open, tag, _i, _ref,

  __slice = [].slice;


tag = "<impossible>";


_ref = tag.split(""), open = _ref[0], contents = 3 <= _ref.length ? __slice.call(_ref, 1, _i = _ref.length - 1) : (_i = 1, []), close = _ref[_i++];




Function binding

- function 을 callback 으로 붙이거나, 다른 object 에 attach 하는 경우 정의할 당시의 this context 는 날아갈 수 있다.

- => 는 function define 은 물론 현재의 this(context) 도 함께 전달한다.

- -> 는 => 와 다르다.


<Context delivering with =>>

Account = (customer, cart) ->

  @customer = customer

  @cart = cart

  $('.shopping_cart').bind 'click', (event) =>

    @customer.purchase @cart

↓  

var Account;


Account = function(customer, cart) {

  var _this = this;

  this.customer = customer;

  this.cart = cart;

  return $('.shopping_cart').bind('click', function(event) {

    return _this.customer.purchase(_this.cart);

  });

};





Embedded JavaScript

- ' ' 안에 써서 assign 하면 compile될 때 conversion 없이 script 언어 그대로가 들어간다.

hi = `function() {

  return [document.title, "Hello JavaScript"].join(": ");

}`

↓   

var hi;


hi = function() {

  return [document.title, "Hello JavaScript"].join(": ");

};





Switch/When/Else


switch day

  when "Mon" then go work

  when "Tue" then go relax

  when "Thu" then go iceFishing

  when "Fri", "Sat"

    if day is bingoDay

      go bingo

      go dancing

  when "Sun" then go church

  else go work

↓ 

switch (day) {

  case "Mon":

    go(work);

    break;

  case "Tue":

    go(relax);

    break;

  case "Thu":

    go(iceFishing);

    break;

  case "Fri":

  case "Sat":

    if (day === bingoDay) {

      go(bingo);

      go(dancing);

    }

    break;

  case "Sun":

    go(church);

    break;

  default:

    go(work);

}





Try/Catch/Finally


try

  allHellBreaksLoose()

  catsAndDogsLivingTogether()

catch error

  print error

finally

  cleanUp()

↓ 

try {

  allHellBreaksLoose();

  catsAndDogsLivingTogether();

} catch (error) {

  print(error);

} finally {

  cleanUp();

}





Chained Comparisons


cholesterol = 127


healthy = 200 > cholesterol > 60

↓  

var cholesterol, healthy;


cholesterol = 127;


healthy = (200 > cholesterol && cholesterol > 60);







String Interpolation, Block String, and Block Comments

- #{…} 내용은 "" 안에서 수식으로 인정된다. ( 변수를 링크할 떄에도 사용 )

- 멀티라인 string은 그냥 string으로 인식.

- 형식을 맞춘 string 이 필요하다면 """ 내용 """ 으로 한다.

- ### 내용 ### 는 블럭 커멘트


< #{ expression } >

author = "Wittgenstein"

quote  = "A picture is a fact. -- #{ author }"


sentence = "#{ 22 / 7 } is a decent approximation of π"

↓  

var author, quote, sentence;


author = "Wittgenstein";


quote = "A picture is a fact. -- " + author;


sentence = "" + (22 / 7) + " is a decent approximation of π";

loadrun: sentence



<String interpolation>

html = """

       <strong>

         cup of coffeescript

       </strong>

       """

↓  

var html;


html = "<strong>\n  cup of coffeescript\n</strong>";



<Block Comment>

###

CoffeeScript Compiler v1.3.3

Released under the MIT License

###

↓  

/*

CoffeeScript Compiler v1.3.3

Released under the MIT License

*/





Block Regular Expressions

- regular expression 은 /// 으로 시작한고, ///로 끝난다.

OPERATOR = /// ^ (

  ?: [-=]>             # function

   | [-+*/%<>&|^!?=]=  # compound assign / compare

   | >>>=?             # zero-fill right shift

   | ([-+:])\1         # doubles

   | ([&|<>])\2=?   # logic / shift

   | \?\.              # soak access

   | \.{2,3}           # range or splat

) ///

↓   

var OPERATOR;


OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?\.|\.{2,3})/;

load






Cake and Cakefiles

- Cake 는 빌드 시스템. Cakefile 에 빌드에 대한 내용이 들어있다. cake [task] 를 통해 빌드를 실행할 수 있다. ( 단순 컴파일은 coffee 로 )





"text/coffeescript" Script Tags

- <script type="text/coffeescript"> 를 통해 browser 에 직접적으로 coffeescript 태그도 들어갈 수 있다. 하지만 이 녀석은 컴파일을 위해 coffee-script.js 를 필요로 한다. 속도 및 메모리를 고려했을 떄 ( 특히 모바일 환경에서는 ) js 로 compile 된 녀석을 사용하는 것이 훨씬 좋다.


도움이 되셨다면 손가락 꾸욱~ ( 로그인 필요 x )



 
반응형

댓글