Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

CoffeeScript의 이터레이션과 클래스

객체
객체의 생성은 아래의 여러가지 방법으로 할 수 있습니다.

obj = new Object()
obj = {}

객체를 정의할 때 중괄호와 콤마는 선택적으로 사용할 수 있습니다.

obj = {
  a:'b'
  b:'c'
}
obj = 
  a:'b'
  b:'c'

다음과 같이 한줄로 작성할 때는 콤마를 생략할 수 없습니다.

obj = a:'b', b:'c'

아래처럼 객체를 정의할때 key와 value의 변수명이 동일하다면 하나로 축약해서 작성할 수 있기 때문에 아래의 두 라인은 동일합니다.

obj = {name: name}
obj = {name}


배열
배열을 아래와 같이 범위(Range)를 통해서 작성할 수 있습니다.

coffee> [1..3]
[ 1, 2, 3 ]
coffee> [1...3]
[ 1, 2 ]

..인 Inclusive Range는 지정된 마지막 값까지를 포함하고 ...인 Exclusive Range는 마지막 값을 포함하지 않습니다.


coffee> [3..1]
[ 3, 2, 1 ]

coffee> [3...1]
[ 3. 2 ]

Range는 위처럼 거꾸로 사용할 수도 있습니다.
배열에서 이 Range를 통해서 SlicingSplicing을 할 수 있습니다.



coffee> [1,2,3,4,5][0..3]
[ 1, 2, 3, 4 ]
coffee> [1,2,3,4,5][0...3]
[ 1, 2, 3 ]
coffee> [1,2,3,4,5][0..-1]
[ 1, 2, 3, 4, 5 ]
coffee> [1,2,3,4,5][0...-1]
[ 1, 2, 3, 4]
coffee> [1,2,3,4,5][1..]
[ 2, 3, 4, 5 ]
coffee> [1,2,3,4,5][1...]
[ 2, 3, 4, 5 ]

위는 Slicing의 예제입니다. 배열위에 Range를 붙히면 범위에 포함되는 배열이 리턴됩니다. Range의 마지막 값에 -1을 사용할 경우 이는 Array.length -1 을 의미하며 마지막 값을 생략하면 .....모두 배열의 마지막까지를 의미합니다.




coffee> a = [1,3]
[ 1, 3 ]
coffee> a[1...1] = 2
2
coffee> a
[ 1, 2, 3 ]

Splicing의 예제입니다. 배열뒤에 붙힌 Range가 비어있다면 Range의 첫 인덱스 위치에 주어진 값이 인서트됩니다.


이터레이션
for key, value of object
  # key, value 사용

객체에 대해서 이터레이션을 할 경우 위의 문법을 사용합니다. key, value는 해당 위치에 객체의 키와 값이 루프마다 할당이 되며 key, value 위치에 할당이 되기 때문에 변수명은 원하는대로 사용할 수 있습니다. value부분은 사용하지 않고 key만 받을 수도 있습니다. 이렇게 이터레이션을 사용할 경우 prototype을 포함한 객체의 모든 프로퍼티에 대해서 이터레이션이 돌게 되는데 prototype을 제외한 프로퍼티에 대해서 이터레이트하려면 for own을 사용하면 됩니다.

for own key of object


for key of object
  continue unless object.hasOwnProperty(key)

위의 코드는 동일합니다. for own을 사용할 경우에는 hasOwnProperty가 true인 경우에만 루프를 돌게 됩니다.

for value in array
  # value 사용

배열에 대해서는 위의 문법을 사용해서 이터레이션을 합니다. 객체와 차이점은 of대신에 in을 사용합니다.
이터레이션을 사용할 때는 when절을 뒤에 붙혀서 when절의 조건이 true일 때만 이터레이션하도록 할 수 있으면 다음과 같이 사용합니다.

for key, value of obj when value is true

추가적으로 for in 에서는 by를 사용해서 이터레이션의 단위를 정할 수 있으며 by는 for of에서는 사용할 수 없습니다.

coffee> for value in [1,2,3,4,5,6,7,8] by 2 then value
[1, 3, 4, 5]
coffee> for value in [1,2,3,4,5,6,7,8] by 3 then value
[1, 4, 7]

배열대신에 Range도 사용할 수 있습니다.

coffee> for value in [1..10] by 2 then value
[1, 3, 4, 5, 7, 9]
coffee> for value in [1..10] by 3/2 then value
[1, 2.5, 4, 5.5, 7, 8.5, 10]
coffee> for value in [10..1] by -2 then value
[10, 8, 6, 4, 2]

위처럼 Range를 쓸때는 by에 분수를 사용할 수도 있으며 음수를 사용해서 이터레이션을 거꾸로 할 수도 있습니다.(배열에서는 분수와 음수는 동작하지 않습니다.)

coffee> obj = name: 'Outsider'
{ name: 'Outsider' }
coffee> 'name' of obj
true
coffee> 'age' of obj
false
coffee> arr = [1, 2, 3, 4, 5]
[ 1, 2, 3, 4, 5]
coffee> 1 in arr
true
coffee> 0 in arr
false

of와 in은 오퍼레이터이기도 하기 때문에 객체와 배열에서 해당 키나 값이 존재하지를 확인하는 데도 사용할 수 있습니다.

coffee> a = 1
1
coffee> a++ while a < 5
[ 1, 2, 3, 4 ]
coffee> a = 1
1
coffee> a++ until a > 5
[ 1, 2, 3, 4, 5 ]

루프는 while과 until을 통해서도 할 수 있으며 until은 while not을 의미합니다.

coffee> a = 1
1
coffee> loop break if a++ > 5
[]
coffee> console.log a
7

위와 같이 loop키워드를 이용해서도 루프를 돌 수 있습니다. 모든 이터레이션 키워드는 postfix와 들여쓰기 형식을 사용할 수 있지만 loop키워드는 prefix만 가능합니다.


Comprehension
Array Comprehension 혹은 List Comprehension을 사용하면 아주 간결하게 코드를 작성할 수 있습니다.

var a = [1,2,3];
var b = [];
for (var i = 0; i < a.length; i++) {
  b.push(a[i]*2);
}
console.log(b);
// [2, 4, 6]

예를 들어 a배열의 2배수의 값을 가지는 새로운 배열을 가지려면 자바스크립트로 위와 같이 작성할 수 있는데 Array Comprehension을 사용하면 아래와 같이 구현할 수 있습니다.

coffee> b = a*2 for a in [1, 2, 3]
[ 2, 4, 6]



패턴 매칭(Destructuring Assignment)

coffee> [a, b, c] = [1, 2, 3]
[ 1, 2, 3 ]
coffee> a
1
coffee> b
2
coffee> c
3

위의 코드에서 좌측은 배열이 아닌 배열 패턴 매칭입니다. 우측에 있는 배열의 각 값이 좌측에 있는 변수로 할당됩니다. 좌우측 모두 변수가 사용가능하므로 Swap을 다음처럼 작성할 수 있습니다.

coffee> a = 1
1
coffee> b = 2
2
coffee> [a, b] = [b, a]
[ 2, 1 ]
coffee> a
2
coffee> b
1


패턴매칭은 객체 패턴매칭도 존재합니다.

coffee> obj = { x:1, y:2 }
{ x: 1, y: 2 }
coffee> {x:a, y:b} = obj
{ x: 1, y: 2 }
coffee> a
1
coffee> b
2

배열 패턴매칭과 동일하게 위 예제에서도 좌측을 객체가 아닌 패턴이고 키:변수명의 형식을 취합니다. 객체의 키의 값이 변수명에 할당됩니다.

coffee> obj = { x:1, y:2 }
{ x: 1, y: 2 }
coffee> {x, y} = obj
{ x: 1, y: 2 }
coffee> x
1
coffee> y
2

만약 할당하려는 변수명과 객체의 키가 동일하다면 위처럼 간단하게 패턴매칭을 할 수도 있습니다.이 패턴매칭은 Destructuring Assignment라고 알려져있고 JavaScript 1.7 표준입니다.


클래스
자바스크립트에서는 모듈화를 할 때 네임스페이스를 사용하게 되고 커피스크립트에서는 각 .coffee파일이 익명함수로 감싸지기 때문에 외부에서는 접근할 수 없습니다. 그래서 공유를 하기 위해서는 명시적으로 전역객체를 가져와서 전역객체에 공유할 값을 노출시켜야 합니다.

root = global ? window

위 코드는 전역 객체를 가져오는 코드이고 global은 node.js에서의 전역객체이고 window는 웹브라우저에서의 전역객체입니다. 이 전역객체에 프로퍼티로 할당해서 모듈간에 공유할 수 있습니다.

A = ->
A::say = 'Hello World'

::는 prototype의 별칭입니다.

자바스크립트는 prototype기반의 언어이기 때문에 Class가 제공되지 않지만 오랫동안 클래스를 사용하려는 시도가 있었고 그 오랜 시도에 따른 표준패턴이 커피스크립트에서 class라는 키워드로 제공됩니다. 클래스 선언은 아래와 같습니다.

class Me
  constructor: ->
    @name = 'Outsider'
    Me.year++

  #Prototpye 프로퍼티
  job: 'Programmer'
  say: -> 'Hello'
  # Class-level 프로퍼티
  @year: 2011
  @post: -> 'posted'

alert Me.year # 2011
alert do Me.post # posted

a = new Me
alert a.job # Programmer
alert do a.say # Hello
alert Me.year # 2012

constructor함수는 prototype 프로퍼티가 아닌 객체를 생성할 때마다 호출되는 생성자 함수입니다.


상속
클래스는 extends 키워드를 이용해서 상속받을 수 있으며 문법은 다음과 같습니다.

class B extends A

B 클래스가 A클래스를 상속받는데 A의 모든 프로토타입과 클래스레벨 프로퍼티들이 복사됩니다.

class A
  constructor: -> 

class B extends A
  constructor: -> super()

위와 같이 사용할 수 있으며 자식 클래스는 super()를 실행해서 부모를 호출할 수 있습니다. 부모의 메서드가 파라미터가 필요할 경우에는 super()대신에 super라고만 작성하면 현재 함수의 파라미터를 부모로 전달합니다.


다형성
커피스크립트에서 다형성은 switch 키워드를 사용해서 구현할 수 있습니다.

ex = (a) ->
  switch a
    when 1,2,3
      console.log 'first'
    when 4
      console.log 'second'
    else
      console.log 'last'

ex 1 # first
ex 4 # second
ex 5 # last

switch문이 자바스크립트와 다른 점은 암묵적으로 when절간에 break을 하기 때문에 의도하지 않게 다음 when절이 실행되는 일이 없고 switch의 결과가 리턴이 됩니다. 또한 case문 대신에 when절을 사용하면 default대신에 else문을 사용합니다. when절에는 매치할 것들이 콤마로 구분되어 여러가지를 사용할 수 있습니다.
2011/08/13 03:48 2011/08/13 03:48