블로그 내 검색

2014. 11. 19.

EcmaScript 6 new Features

시작하며

아직 프로그래머들에겐 EcmaScript5 도 익숙하지 않는 판국에, EcmaScript6 버전 (이하 ES6)을 사용하는 노력들이 여기저기서 보인다.

몇몇의 오픈소스들은 이미 ES6 을 사용한 버전을 저장소에 올려두기도 했으며, ES6의 매력적인 일부분을 EC5에서 에뮬레이팅 하는 프레임워크도 나오고 있다
(Co, Koa 등등...)

예전에 EcmaScript5의 기능을 소개해본적 이 있는데, 이번에도 한번 정리해보려고 글을 쓰기 시작했다. 쉽지 않다...

실전 활용은 전혀 무리지만, 그래도 이런거구나 하는 마음으로 새로운 기능을 몇가지만 살펴보자

선언, 할당

변수 선언  방법이 다양해졌다
이러한 방법으로 변수 선언이 가능하다.
var [a, b, c] = [1, 2, 3];
console.log(a + c) // 4

var [ fullMatchNumber, firstNumber , middleNumber, lastNumber ] = 
    "010-8686-3131".match(/^(\d{2,3})-(\d{3,4})-(\d{4})$/);
console.log(middleNumber); // "8686"
객체 리터럴 선언 시 귀찮은 키와 값 매핑을 좀 더 간단히 해결할 수 있게 됐다
var honkong = "홍콩", china = "중국";
var country = { honkong, china };
console.log(country.china); // "중국"
객체의 키를 사용한 선언도 가능하다.
var Cat = {
    name: "Kitty",
    age: 3
};
var { name, age } = Cat;
console.log(name); // "Kitty"
서버에서 가져온 고객 데이터 중 세번째 고객의 첫번째 주문항목과 고객의 이름을 변수로 한번에 선언하고 싶다고 한다면
var fromServerData = [
    { 
        name: "pooh", 
        orderList: [ '신발', '구두', '김치'   ] 
    },
    { 
        name: "hunky", 
        orderList: [   '코트'   ] 
    },
    { 
        name: "pang", 
        orderList: [  '가글', '시계'  ] 
    },
    { 
        name: "jj", 
        orderList: [ '자전거', '셔츠' ] 
    }
]
다음과 같이 간단히 쓸 수 있다. (유용한지는 모르겠다...)
// 콤마를 사용해서 앞선 두 인덱스를 건너뛰고 그 뒤부터 변수를 선언해준다.
var [ , , { name: vipName, orderList: [firstOrderName] }] = fromServerData;
console.log(vipName) // "pang"
console.log(firstOrderName) // "가글"
물론 함수 인자에도 위에서 소개한 변수 선언법을 적용해볼 수 있다.
함수 인자도 결국 함수의 변수 선언이나 마찬가지니...
function introduce({ firstName, lastName }) {
    return lastName + ", " + firstName;
};
var ironMan = {
    firstName: "스타크",
    lastName: "토니"
}
console.log(introduce(ironMan)) // "토니, 스타크"

함수 기본값

예전에는 함수로 전달되는 인자에는 기본값을 줄 수 없어서 항상 함수 바디에서 인자에 값이 있는지 판단하고, 없다면 대체하는 코드가 필수로 들어가야 했지만, ES6에서는 함수 인자부분에 기본값을 설정할 수 있다~!!
function Rectangle(width=100, height=100) {
    this.width = width;
    this.height = height;
};
console.log(new Rectangle().width) // 100

가변 변수 (...rest)

자바등에서 즐겨 쓰이는 가변변수 가 더욱 유용하게 추가되었다.
function vargs(a, ...b) { 
    console.log(Array.isArray(b)); // 자바와 같이 가변 부분은 배열이다.
    b.push(a);
    return b;
}

var ret = vargs(1,2,3,4,5);
console.log(ret); // "2,3,4,5,1"
배열의 요소로 활용할 수도 있는 등, 유연하다.
var a = [1,2];
var b = [3,4, ...a];
console.log(b) // 3,4,1,2

let

자바스크립트는 언어 자체가 블럭 스코프가 존재하지 않는다.
하지만 가끔 블럭 스코프 등으로 스코프를 제한해야 할 때가 있는데, 그 때는 익명 함수를 사용하여 (여러 문제가 있는) 문제를 우회했지만 ES6에서는 굉장히 간단해졌다.
// ES5. 함수 스코프란 이렇다...
var ret = [];
for(var i = 0; i < 10; i++) {
    var myIndex = i;
    ret.push(function(){ return myIndex; })
}
ret[0](); // 9
ret[1](); // 9
ret[2](); // 9
ret[9](); // 9
함수 스코프와 그에 따른 클로저로 인해 배열의 모든 함수는 같은 스코프 체인을 생성하게 되어 다들 같은 마지막 루프의 인덱스 값만 반환하는 상황이 나온다.

클로저의 예제로 많이 나오는 예제라 몇몇사람은 친숙할 것이다.

물론 이것을 해결하는 방법도 함수 스코프를 사용하는 방법이다.
var ret = [];
for(var i = 0; i < 10; i++) {
    
    // Scope 를 억지로 생성, 정말 억지스럽다...
    (function() {
        var myIndex = i;
        ret.push(function(){ return myIndex; });
    })();
}
ret[0](); // 0
ret[1](); // 1
ret[2](); // 2
ret[9](); // 9
let 키워드를 사용하면 저런 삽질이 필요없다. 스코프가 블럭으로 제한되게 된다.
// ES6
var ret = [];
for(var i = 0; i < 10; i++) {

    // var 대신 let 으로 변수를 선언한다
    let myIndex = i;
    ret.push(function(){ return myIndex; })
}
ret[0](); // 0
ret[1](); // 1
ret[2](); // 2
ret[9](); // 9

FOR-OF 

for-of 로 루프가 간단해졌다. (정말?)
var arr = [ 1, 2, 3, 4, 5, 6 ];
[ for(x of arr) console.log (x) ] // 6번 루프를 돌며 배열 요소를 하나씩 출력한다.
for - of 구문은 실행 결과로 루프당 실행한 표현식의 결과를 반환하는데, 위의 예제의 경우 console.log 함수는 아무것도 반환하지 않으므로 undefined 로 채워진 길이 6의 배열을 반환하게 될 것이다.

조금 응용하여 다음과 같이 조건부로 반환하게 할 수 있다. 3 이상의 숫자만을 배열로 반환시켜보자
var arr = [ 1, 2, 3, 4, 5, 6 ];
var filtered = [ for(x of arr) if(x > 3) x ];
console.log(filtered); // 4, 5, 6
다중 루프도 해볼 수 있다.
var arr1 = ["a", "b" ], arr2 = [ "A", "B" ];
var ret = [ for (i of arr1) for (j of arr2) i+j ];

console.log(ret); // "aA, aB, bA, bB"
[] 괄호로 내부의 for - of 를 감싸주면 결과가 다르다.

내부 루프의 결과가 배열([]) 로 전달되기 때문이다.
var ret = [ for (i of arr1) [ for (j of arr2) i+j ] ];

console.log(ret); // "[[aA, aB], [bA, bB]]"

클래스

스펙 상 class 키워드로 클래스를 생성해볼 수 있으나 아직 FireFox, Chrome 에 탑재된 엔진에서도 클래스 지원은 하고 있지 않다. (Feature Not Yet Supported) 규약에 쓰인 내용을 간추려 코드로 써보면 이렇다.
class Car {

    // 생성자.
    constroctor(name) {
        this.name = name;
    }

    drive() {
        return "자동차를 운전합니다";
    }
}

class Truck extends Car {

    constroctor(name) {

        // 생성자 체이닝
        super(name);
    }
        
    drive() {
        // 부모의 drive 호출
        var d = super();
        return "무섭게 " + d;
    }
}

var truck = new Truck("포터");
truck.drive(); // "무섭게 자동차를 운전합니다"

Generator

제네레이터는 함수의 동작 중 순간순간을 정지하거나(suspend) 다시 동작(next)시킬 수 있는 순차 실행기 (Iterator) 를 반환하는 함수이다.

제네레이터 생성 문법에는 function *(){} 와 같이 asterisk(*) 를 써야 한다. 중단점에는 yield 키워드를 사용한다.
function* sequencer() {
    var i = 0;
    while(true) {
        yield i++;
    }
}
var gen = sequencer();
gen.next(); // Object { value: 0, done: false }  
gen.next(); // Object { value: 1, done: false } 
...
제네레이터 함수를 실행하면 이터레이터(Iterator)가 반환된다.

이터레이터의 next() 메서드를 실행할 때마다 함수가 실행되며, value와 done 프로퍼티를 가지는 객체를 반환한다. value 는 yield의 표현식이 할당되며 done 속성으로 next 가 가능한지 여부를 알 수 있다.

제네레이터는 비동기 작업과 결합할 경우 굉장히 강력한 능력을 낸다.
혹시 C# 을 해본 사람이라면 await 키워드를 사용한 비동기 프로그래밍과 약간(..) 비슷하다고 생각될 것이다.
function work(gen) {
    var ret;
    while(!ret.done) {
        ret = gen.next();
    }
    return ret.value;
}
function *asyncJob() {

   var input = yield getJSON("/some.json");
   return yield getJSON("/thing.json", input);
}
var asyncGen = work(asyncJob());
위의 구문이 별거 아닌 것처럼 느껴지지만, 만일 예외 처리가 섞인다면 이야기가 달라진다. 일반적인 비동기의 예외 처리는 콜백을 동반하기에 굉장히 어렵다. 하지만 제네레이터를 사용하게 되면 다음과 같이 동기식 코드를 사용하는 것처럼 단순해진다.
function *asyncJob() {
   
    // 단순히 그냥 try - catch.
    try {
        var input = yield getJSON("/some.json");
        return yield getJSON("/thing.json", input);
    }
    catch(ex) {
        return handleError(ex);
    }
}

이 외에도...

이 외에도 Promise가 정식으로 합류(?)했으며, Arrow Function, Module 등등 여러 새로운 기능들이 있지만 다 소개하기엔 블로그 스크롤과 읽는 사람의 인내심이 무한히 길어지고 한계가 올 것 같다. (글 쓰는 사람도 한계다...)

기회가 된다면 다음 포스팅에...

위의 예제들은 클래스를 제외하고는 전부 Firefox, Chrome  Console에서 실행해볼 수 있다.
크롬은 대신 주소창에 chrome://flags 을 타이핑해서 실험실에 들어간 뒤 "enable-javascript-harmony" 기능을 활성화시켜줘야 할 수 있다.

댓글 없음:

댓글 쓰기