[javascript] ES6+ 기초 문법

es6, ecmascript6, es6+

javascript의 구성은 BOM(Broswer Object Model), DOM(Document Object Model) 그리고 이러한 것들을 다루는 문법인 ECMAScript가 존재합니다. 그리고 ES6 부터 javascript는 크게 변화했습니다. 이번 포스트에서는 javascript의 ES6+(ECMAScript6+) 의 문법(스펙)에 대해 알아보도록 하겠습니다. 원래 ESMAScript란 javascript의 문법 혹은 spec 입니다.

  1. Variable
  2. Destructuring
  3. Template String
  4. Arrow Function
  5. function parameter
  6. Class
  7. for of
  8. Object 할당 문법

사실 이 외에도 굉장히 많은 문법과 Spec이 존재합니다. 그러나, javascript에 대한 깊은 이해가 필요한 것들이기 때문에 기초적인 문법 위주로 알려드리도록 하겠습니다.


Variable

Variable, 변수입니다. 기존의 Javascript(통칭 ES5)에서는 var 라는 키워드를 변수로 사용했습니다.

var a = 10

이렇게 var로 선언을 하면 hoisting, scope 등이 es6와 다르게 작동합니다. var를 사용하면 hoisting이 발생하고, function 단위의 scope를 사용합니다.
(자세한 내용은 hoisting, scope, closure 포스트를 참고해주세요)

그리고 es6에서는 let, const 라는 키워드를 사용합니다.

let a = 10    // let에는 새로운 값을 넣을 수 있다.
const b = 20  // const는 상수이며, const로 선언한 변수는 재 할당 불가
a = 30
b = 20 // error

const와 let을 사용하면 hoisting이 발생하지 않으며 block 단위의 scope를 사용할 수 있습니다.
(자세한 내용은 hoisting, scope, closure 포스트를 참고해주세요)


Destructuring

Structure : 구조

Structuring : 구조화

Destructure : 구조 파괴

Destructuring : 구조화 파괴

단어의 뜻은 이 처럼 정의됩니다. 그래도 잘 이해가 안 돼죠? 더 풀어서 설명하자면 Destructuring은 "구조 분해 할당" 이라고 합니다.
Object나 Array로 구조화된 변수를 다시 분해 시켜 할당한다 정도로 이해하면 좋을듯합니다.

// array default
const [a, b] = [10, 20]
console.log(a, b) // result: 10, 20

// array iterator
const [c, d, ...rest] = [10, 20, 30, 40, 50];
console.log(rest) // [30, 40, 50]

// object default
const obj = {k1: 10, k2: 'string', k3: [10, 20, 30]}
const {k1, k2, k3} = obj
console.log(k1, k2, k3) // result: 10, 'string', [10, 20, 30]

// object iterator
const {k1, ...obj2} = obj
console.log(obj2) // {k2: 'string', k3: [10, 20, 30]}

...rest 같은 형태를 iterator 라고 말합니다. 이것도 es6에 추가된 스펙이지만, 처음엔 이해하기 좀 난해하므로 그냥 이런게 있다 정도로만 여기고 넘어갑시다.

기본 값 설정도 가능하며, 매칭이 되지 않으면 undefined로 선언됩니다.

const [a, b] = [1];
console.log(a); // 1
console.log(b); // undefined

const [a=5, b=7] = [1];
console.log(a); // 1
console.log(b); // 7

변수 값을 교환할 수도 있습니다.

let a = 1, b = 3

[a, b] = [b, a]
console.log(a) // 3
console.log(b) // 1

함수를 통해 반환하는 것도 가능합니다.

function f() { return [1, 2] }
const [a, b] = f()
console.log(a) // 1
console.log(b) // 2

다음과 같이 관심 없는 반환값을 무시할 수 있습니다.

function f() { return [1, 2, 3] }
const [a, , b] = f()
console.log(a) // 1
console.log(b) // 3

객체로부터 속성을 해체하여 객체의 원래 속성명과는 다른 이름의 변수에 할당할 수 있습니다.

const obj = {p: 42, q: true}
const {p: foo, q: bar} = obj // p는 foo로, q는 bar로 교체합니다.
console.log(foo); // 42
console.log(bar); // true

Template String

Template String은 그레이브 문자(혹은 backquote) ` 를 이용하여 문자열을 선언하는 방법입니다.

// es5+
var num = 10
var str = '\n
  따옴표나 쌍따옴표를 사용하면 엔터 문자는 이렇게 \n
  사용해야 하며, 문자열 내부에 변수를 사용할 때도 \n
  num2 : '+num2+' 이런 방식을 사용해야합니다. \n
'

// es6+
const num2 = 10
const str2 = `
  backquote를 이용하여 선언하면
  이처럼 엔터 문자를 사용할 수 있으며
  변수도 사용할 수 있습니다.
  num2 : ${num2}
  또한 '' "" 이렇게 따옴표를 맘대로 사용해도 됩니다.
`

아무리 생각해도 굉장히 편리한 문법입니다.


Arrow Function

arrow function, 다른 말로 화살표 함수 입니다. es5에서 function은 사실 method로서의 기능도 가지고 있습니다. 하지만 arrow function의 경우 순수하게 function으로서의 기능만 수행합니다.

method에서는 this를 사용할 수 있고, function에서는 this를 사용할 수 없습니다.

es5와 es6+를 비교하여 살펴보도록 하겠습니다.

// es5
function a (num) {
  var res = num * 10
  return res
}
a(10) // 100
// es6+
const a = (num) => {
  const res = num * 10
  return res
}
a(10) // 100
// 매개변수가 1개일 때는 괄호를 생략해도 됩니다.
const a = num => {
  const res = num * 10
  return res
}
// 함수가 1줄일 경우 다음과 같이 사용할 수 있습니다.
const a = num => num * 10
// 이것은 다음과 같습니다.
// var a = function (num) { return num * 10 }

다른 spec과 사용하면 더 효율적입니다.

const arr = [10, 20, 30]
arr.map(v => v * 10)
console.log(arr) // 100, 200, 300

참고로 array map은, 배열을 반복하여 return한 값을 새로운 값으로 대체하는 es6+ 에서 사용되는 method 입니다.

destructuring과 함께 이렇게 사용할 수도 있습니다.

var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
f();  // 6
// a와 b에는 1, 2가 할당되고
// c 에는 a + b, 즉 3이 할당됩니다.
// 그 다음에 a+b+c =  6을 반환합니다.

그리고 es5의 function 에서는 this를 사용할 수 있습니다.

function Person() {
  // Person() 생성자는 `this`를 자신의 인스턴스로 정의.
  this.age = 0

  function growUp() {
    // growUp() 함수는 `this`를 전역 객체로 정의
    // 이는 Person() 생성자에 정의된 `this`와 다름.
    this.age++ // 즉, NaN이 출렴됨
  };
  setInterval(growUp, 100)
}

var p = new Person()
// RESULT : NaN NaN NaN ...

그래서 ES5에서는 다음과 같이 코딩해야합니다.

function Person() {
  var that = this;  
  that.age = 0;

  setInterval(function growUp() {
    // 콜백은  `that` 변수를 참조하고 이것은 값이 기대한 객체이다.
    that.age++;
  }, 1000);
}

하지만 arrow function의 경우 this를 취급하지 않기때문에 아래와 같이 코딩해도 상관 없습니다.

function Person(){
  this.age = 0;
  setInterval(() => {
    this.age++ // 여기서 가르키는 this는 person 객체의 this입니다.
  }, 100);
}

const p = new Person();

반대로, 객체 내에서 Arrow Function을 사용할 경우, 다음과 같은 현상이 발생합니다.

var obj = {
  i: 10,
  b: () => console.log(this.i, this),
  c: function() {
    console.log( this.i, this)
  }
}
obj.b(); // prints undefined, Window // method가 아니기 때문에 this를 사용할 수 없습니다.
obj.c(); // prints 10, Object {...}

또한 arrow function은 생성자로 사용될 수 없습니다.

const Foo = () => {};
const foo = new Foo(); // TypeError: Foo is not a constructor

객체를 반환할 경우 괄호로 감싸야 합니다.

const func = () => {  foo: 1  };               // func() 호출은 undefined를 반환!
const func = () => {  foo: function() {}  };   // SyntaxError: function 문은 이름이 필요함

const func = () => ({ foo: 1 });

화살표 함수는 굉장히 응용 범위가 굉장히 다양하고 편리합니다. 꼭 익혀둬야 할 문법입니다.

Function Parameter

ES6+에서는 function의 parameter(매개변수)에 다음과 같은 일들이 가능합니다.

Default 지정

// 매개변수의 기본 값을 지정할 수 있습니다.
const f = (x, y=10) => x + y
f(3) // 13
f(10, 5) // 15



Rest

// 가변인자를 사용가능하며, 배열로 치환시켜 줍니다.
// 여기서 y는 갯수와 상관 없이 항상 배열로 치환됩니다.
const f = (x, ...y) => x * y.length
f(10, 2, 3, 4, 5) // 10 * 4 = 40
f(10, 2) // 10 * 1 = 10



Spread

// 인자를 Rest로 전달하여 사용 가능합니다.
const f = (x, y, z) => x + y + z
const arr = [10, 20, 30]
f(...arr) // 10 + 20 + 30 = 60
f(...[1,2,3]) // 6



Class

es6+ 에서 부터는 class 문법을 지원합니다. 하지만 여전히 프로토타입을 통해 동작합니다.

class Foo {
  constructor () { }
  getA () { return this.a }
  setA (v) { this.a = v}
}
const foo = new Foo()
foo.setA(10)
console.log(foo.getA())

다음과 같이 사용할 수도 있습니다.

const Foo = class {
  constructor () { }
  getA () { return this.a }
  setA (v) { this.a = v}
}
const foo = new Foo()

상속도 지원합니다.

const Foo = class {
  constructor () { this.a = 10 }
  getA () { return this.a }
  setA (v) { this.a = v}
}
const Foo2 = class extends Foo {
  // 상속을 사용할 경우 constructor에 super()를 사용해야합니다.
  // super()는 부모의 생성자(Construcotr) 입니다.
  constructor () { super(); }
  getB() { return this.b }
  setB(v) { this.b = v}
  
}
const foo2 = new Foo2()
foo2.setA(10)
foo2.setB(20)
console.log(foo2.getA(), foo2.getB()) // 10 20

static Method를 사용할 수 있습니다.

const Foo = class {
  constructor () {  }
  getName () { console.log('this is method of instance') }
  static getName () { console.log('this is method of Foo Class') }
}
var foo = new Foo()
Foo.getName()
foo.getName()

static 변수는 다음과 같이 사용할 수 있습니다.

const Foo = class {
  constructor (a,b) { this.a = a; this.b = b }
  getVal () { return this.a + this.b}
  static getVal () { return Foo.a + Foo.b }
  static init () { Foo.a = 10; Foo.b = 20}
}
var a = new Foo(1, 2)
var b = new Foo(3, 4)
a.getVal() // 3
b.getVal() // 7
Foo.getVal() // 에러
Foo.init() // static 변수 초기화
Foo.getVal() // 30

하지만 private method와 private 변수는 사용할 수 없습니다. 최신 ecmascript spec에 추가되긴 했지만.. 이용될 수 있을지 의문입니다.


for of

es5에는 for in 이라는 구문이 있습니다. object의 key 값으로 반복하는 것입니다.

for (var k in {a: 1, b: 2}) { console.log(k) } // a, b
for (var k in [10, 20, 30]) { console.log(k) } // 0, 1, 2

es6+에는 for of가 도입되었습니다. 이것은 배열에 대한 값을 출력합니다.

for (const v of [10, 20, 30]) { console.log(v) } // 10, 20, 30

기존의 forEach와 비슷하지만, forEach는 function을 사용한다는 점에서 차이가 있습니다.

for (const v of [10, 20, 30]) {
  console.log(v)
  if (v === 10) return
}
// 10이 출력되고 끝남

[10, 20, 30].forEach(v => {
  console.log(v)
  if (v === 10)  return
})
// 10, 20, 30 모두 출력

즉, for of는 반복 도중 종료할 수 있고 forEach는 불가능합니다.

Object 할당 문법

es6에서는 object에 값을 할당 할 때, key의 name과 value의 variable name이 같을 경우, 생략할 수 있습니다.

// es5
var a = 10, b = 20, c = 30
var obj = { a: a, b: b, c: c}
// es6
const a = 10, b = 20, c= 30
const obj = {a, b, c}

그리고 object의 key에 function을 할당할 때 다음과 같이 사용할 수 있습니다.

// es5
const obj = {
  objName: 'obj',
  fn: function () {
    console.log('Object의 이름은 ' + this.objName + '입니다.')
  }
}
// es6
const obj = {
  objName: 'obj',
  fn () { console.log(`Object의 이름은 ${this.objName} 입니다.`) },
  fn2: () => 10 // 이렇게 arrow function을 사용할 경우 this key는 사용할 수 없습니다.
}



ETC

es6 spec은 모든 브라우저에서 지원 하는 것이 아닌, 일부 브라우저(chrome, firefox 등)에서 지원합니다. 그래서 es6 spec으로 작성한 문서를 babel이라는 도구를 통해서 es5 spec으로 변환하여 사용해야 합니다. 이에 대한 내용은 차후에 다루도록 하겠습니다.