이 글은 Adrian Mejia의 Overview of JavaScript ES6 features (a.k.a ECMAScript 6 and ES2015+)을 번역한 글입니다. 오타나 오역 제보는 언제나 환영입니다!
자바스크립트는 지난 몇 년간 꽤 많은 변화가 있었습니다. 지금 당장 쓸 수 있는 새로운 12가지 기능은 다음과 같습니다.
1. JavaScript History
이번에 자바스크립트에 추가된 기능을 ECMAScript 6 이라고 부릅니다. ES6 나 ES2015+ 라고도 불리죠.
1995년 자바스크립트 개념이 도입된 이래로, 자바스크립트는 서서히 진화해 오고 있었습니다. 기능을 추가하는 작업은 몇 년에 한번씩 일어나곤 했습니다. ECMAScript는 자바스크립트의 이러한 방향을 바로 잡기 위해 1997년에 등장했습니다. ECMAScript는 ES3, ES5, ES6 등과 같은 버젼을 발표해 왔습니다.
보시다시피, ES3, ES5 와 ES6은 각각 10년과 6년의 간격이 있습니다. 새 모델은 매년 작고 점진적인 변화를 만드는 것입니다. ES6 처럼 한 번에 엄청난 변화를 하는것 대신에요.
2. Browsers Support
모든 현대 브라우저와 환경은 ES6를 이미 지원하고 있습니다!
source: https://kangax.github.io/compat-table/es6/
Chrome, MS Edge, Firefox, Safari, Node와 많은 다른 환경에서 이미 자바스크립트 ES6의 기본적인 기능을 지원합니다. 그래서 당신이 이제 배울 모든 튜토리얼은 당장 사용해 볼 수 있습니다.
ECMAScript 6를 시작해봅시다!
3. Core ES6 Features
브라우저 콘솔에서 모든 코드들을 테스트 할 수 있습니다.
그러니 글만 읽지 말고 ES5와 ES6 예제를 테스트 해보길 바랍니다. 어서 해보죠 💪
3.1 Block scope variables
ES6에서 변수를 선언하기 위해 var
대신 let
/const
를 사용합니다.
var
에 무슨 문제가 있었을까요?
var
의 문제는 for
문이나 if
문과 같은 코드 블록에서 변수가 누출되는 것입니다.
ES5
1 | var x = 'outer'; |
test(false)
에서 outer
가 반환되길 기대했지만, undefined
를 얻었습니다.
왜 일까요?
왜냐하면 if
블록이 실행되지 않더라도 네째 줄의 var x
가 호이스트(hoisted)되기 때문입니다.
var 호이스팅:
var
는 함수 범위(scope)입니다. 이것은 선언되기 전에도 전체 함수에서 사용할 수 있습니다.- 선언(Declarations)은 호이스트(Hoisted)됩니다. 그래서 선언되기 전에도 변수를 사용할 수 있습니다.
- 초기화(Initializations)는 호이스트되지 않습니다. 만약
var
를 사용한다면 항상 맨 위에 변수를 선언하세요.- 호이스팅 규칙을 적용해 본 후에 무슨 일이 일어났는지 더 잘 이해할 수 있습니다.
ES5
1
2
3
4
5
6
7
8
9
10> var x = 'outer';
> function test(inner) {
> var x; // HOISTED DECLARATION
> if (inner) {
> x = 'inner'; // INITIALIZATION NOT HOISTED
> return x;
> }
> return x;
> }
>
ECMAScript 2015가 도와주러 왔습니다:
ES6
1 | let x = 'outer'; |
var
를 let
으로 변경하면 예상대로 작동합니다. if
블록이 호출되지 않는다면, 변수 x
는 블록 밖으로 호이스트되지 않습니다.
호이스팅(hoisting) 과 “temporal dead zone”
- ES6에서
let
은 블록의 맨 위로 호이스트 됩니다.(ES5 처럼 함수의 맨위가 아닌)- 그러나, 변수가 선언되기 전에 블록에서 변수를 참조하는 것은
ReferenceError
를 초래합니다.let
은 차단된 스코프를 가집니다. 선언되기 전에 사용할 수 없습니다.- “Temporal dead zone”은 블락의 처음부터 변수가 선언되기 전까지의 영역입니다.
IIFE
IIFE를 설명하기 전에 예제를 봅시다. 여길 봐주세요:
ES5
1 | { |
보시다시피, private
은 유출됩니다. 그것을 감싸기 위해 IIFE (immediately-invoked function expression)을 사용할 필요가 있습니다:
ES5
1 | (function(){ |
jQuery/lodash 나 다른 오픈소스 프로젝트를 살펴보면 그것들은 전역 환경의 오염을 피하기위해 IIFE를 사용하고, 단지 _
, $
및 jQuery
만 전역으로 정의합니다.
ES6에서 더 깔끔합니다. 블록과 let
을 사용하면 더이상 IIFE를 사용할 필요가 없습니다.
ES6
1 | { |
Const
변수를 전혀 변경하지 않는다면 const
를 사용할 수 있습니다.
밑줄:
var
를 버리고let
과const
로.
- 모든 참조에
var
의 사용을 피하고const
를 사용하세요.- 만약 참조를 재 정의해야 한다면
const
대신let
을 사용하세요
3.2 Template Literals
템플릿 리터럴을 사용하면 더이상 중첩된 연결을 하지 않아도 됩니다. 여길 보세요:
ES5
1 | var first = 'Adrian'; |
이제 우린 역 따옴표( ` )와 문자열 보간 ${}
을 사용할 수 있습니다:
ES6
1 | const first = 'Adrian'; |
3.3 Multi-line strings
아래처럼 더이상 문자열과 \n
을 연결하지 않아도 됩니다.
ES5
1 | var template = '<li *ngFor="let todo of todos" [ngClass]="{completed: todo.isDone}" >\n' + |
ES6에서 역 따옴표를 사용하여 이를 해결할 수 있습니다.
ES6
1 | const template = `<li *ngFor="let todo of todos" [ngClass]="{completed: todo.isDone}" > |
두 코드는 정확히 똑같은 결과를 가지게 됩니다.
3.4 Destructuring Assignment
ES6 비구조화(Destructuring)은 매우 유용하고 중요합니다. 아래 예제를 보세요:
배열에서 요소 가져오기
ES5
1 | var array = [1, 2, 3, 4]; |
다음과 같습니다:
ES6
1 | const array = [1, 2, 3, 4]; |
값 치환
ES5
1 | var a = 1; |
다음과 같습니다:
ES6
1 | let a = 1; |
여러 반환 값에 대한 비구조화
ES5
1 | function margin() { |
세째 줄에서, 다음과 같은 배열로 리턴할 수도 있습니다.
1 | return [left, right, top, bottom]; |
하지만 호출자(caller)는 반환된 데이터의 순서에 대해 생각해볼 필요가 있습니다.
1 | var left = data[0]; |
ES6에서, 호출자는 필요한 데이터만 설택할 수 있습니다. (여섯째 줄):
ES6
1 | function margin() { |
Notice: 셋째 줄에서, 우리는 ES6의 새로운 기능을 사용하고 있습니다. { left: left }
를 { left }
로 줄일 수 있습니다. ES5 버젼과 비교하면 얼마나 간결한지 보이시죠. 멋지지 않나요?
일치하는 매개변수에 대한 비구조화
ES5
1 | var user = {firstName: 'Adrian', lastName: 'Mejia'}; |
다음과 같습니다.(하지만 더 간결한)
ES6
1 | const user = {firstName: 'Adrian', lastName: 'Mejia'}; |
Deep Matching
ES5
1 | function settings() { |
다음과 같습니다.(하지만 더 간결한)
ES6
1 | function settings() { |
이것은 객체 비구조화라고도 불립니다.
보시다시피, 비구조화는 매우 유용하고 좋은 코딩 스타일을 가지게 합니다.
모범 사례:
- 배열 비구조화를 사용하여 요소를 가져오거나 값을 바꾸길 바랍니다. 임시 참조를 만들지 않아도 되게 도와줍니다.
- 여러 값을 반환할 때 배열 비구조화를 사용하는 대신 객체 비구조화를 사용하세요.
3.5 Classes and Objects
ECMAScript 6에서, “constructor functions” 🔨 대신 “classes” 🍸 를 사용합니다.
자바스크립트에서 모든 단일 객체는 또 다른 객체인 프로토타입을 가지고 있습니다. 모든 자바스크립트 객체는 그들의 프로토타입의 메서드와 프로퍼티를 상속받습니다.
ES5에서, 우리는 다음과 같이 객체를 생성하기 위해 생성자 함수를 사용하여 객체 지향 프로그래밍(OOP)를 했습니다.
ES5
1 | var Animal = (function () { |
ES6에는 몇가지 달콤한 문법이 존재합니다. 우리는 적은 보일러 플레이트와 새로운 키워드인 class
와 constructor
를 사용해 같은 작업을 수행할 수 있습니다. 또한 우리가 constructor.prototype.speak = function ()
와 speak()
메서드를 얼마나 명확하게 정의했는지 확인해보세요:
ES6
1 | class Animal { |
보시다시피, 두 스타일 (ES5/6)은 동일한 결과를 만들어 내고 같은 방식으로 사용됩니다.
모범 사례:
- 항상 클래스 문법을 사용하고 프로토 타입을 직접 조작하는 것을 피하세요. 왜냐구요? 왜냐하면 코드가 더 간결해지고 이해하기도 더 쉬워지기 때문입니다.
- 빈 생성자를 가지는 것을 피하세요. 클래스는 생성자가 지정되지 않았다면 기본 생성자를 가집니다.
3.6 Inheritance
이전 Animal
클래스를 기반으로 진행하겠습니다. 우리는 그것을 확장하여 Lion
클래스를 정의하기를 원합니다.
ES5에서, 이것은 프로토 타입 상속과 조금 더 관련이 있습니다.
ES5
1 | var Lion = (function () { |
모든 디테일한 사항들을 검토하지 않을겁니다:
- 셋째 줄에서 우리는
Animal
생성자를 매개변수와 함께 명시적으로 호출합니다. - 7-8 줄에서
Lion
프로토 타입을Animal
의 프로토 타입으로 지정했습니다. - 11 줄에서 부모 클래스인
Animal
에서speak
메서드를 호출합니다.
ES6에서 새 키워드인 extends
와 super
가 있습니다.
ES6
1 | class Lion extends Animal { |
이 ES6 코드가 같은 동작을 하는 ES5 코드와 비해 얼마나 읽기 쉬운지 보세요. 승리!
모범 사례:
- 상속을 위해 내장되어 있는
extends
를 사용하세요
3.7 Native Promises
콜백 헬(Callback Hell) 👹 대신 프러미스(Promise) 🙏 를 사용합니다.
ES5
1 | function printAfterTimeout(string, timeout, done){ |
우리는 done
일 때 실행하기 위해 콜백을 받는 함수를 가지고 있습니다. 우리는 두번씩 차례대로 실행해야 합니다. 그것이 콜백에서 printAfterTimeout
를 두 번째로 호출한 이유입니다.
이것은 세 번째나 네 번째 콜백이 필요할 경우 매우 빨리 동작하게 됩니다. promise가 어떻게 동작하는지 봅시다:
ES6
1 | function printAfterTimeout(string, timeout){ |
보시다시피, promise
를 사용하면 다른 함수가 완료된 후에 무언가를 하기 위해 then
을 사용할 수 있습니다. 더이상 중첩 함수를 사용할 필요가 없어집니다.
3.8 Arrow functions
ES6는 함수 표현식을 제거하지 않았지만, 화살표 함수(arrow functions)라고 불리는 것을 새로 추가했습니다.
ES5에서 this
의 문제가 몇가지 있었습니다:
ES5
1 | var _this = this; // need to hold a reference |
함수를 내부에서 참조하거나 bind
를 사용하기 위해 임시로 this
를 사용해야만 합니다. 하지만 ES6에서 화살표 함수를 사용할 수 있습니다.
ES6
1 | // this will reference the outer one |
3.9 For…of
for
나 forEach
대신 for...of
를 사용합니다.
ES5
1 | // for |
ES6 for...of
또한 반복 작업을 수행할 수 있습니다.
ES6
1 | // for ...of |
3.10 Default parameters
변수가 정의되었는지 확인하는 것 대신 default parameters
로 값을 지정할 수 있습니다. 전에 이렇게 한 적이 있나요?
ES5
1 | function point(x, y, isFlag){ |
아마도 변수가 값을 가지고 있는지 확인하거나 기본 값을 할당하는 일반적인 패턴입니다. 그러나 몇 가지 문제가 있는 것을 확인하세요:
- 여덟째 줄,
0, 0
을 넘겨주고0, -1
을 받게됩니다. - 아홉째 줄,
false
를 넘겨주고true
를 받게됩니다.
기본 매개변수로 부울 값을 갖거나 값을 0으로 설정한다면 작동하지 않습니다. 왜 그런지 아시나요? ES6 예를 들어 설명해 드리겠습니다. ;)
ES6를 사용하면 더 적은 코드로 더 좋게 할 수 있습니다!
ES6
1 | function point(x = 0, y = -1, isFlag = true){ |
다섯째 줄과 여섯째 줄은 예상대로 결과를 얻었습니다. ES5 예제는 동작하지 않았었죠. 우리는 false
, null
, undefined
과 0
은 거짓 값이므로 undefined
를 먼저 확인해야 합니다. 숫자는 잊을 수 있습니다.
ES5
1 | function point(x, y, isFlag){ |
undefined
를 확인할 때 비로소 예상대로 동작하게 됩니다.
3.11 Rest parameters
매개변수에서 나머지 매개변수와 전개 연산자로 왔습니다.
ES5에서 매개변수에서 임의의 변수를 얻는 것은 어렵습니다.
ES5
1 | function printf(format) { |
우리는 나머지 매개변수 ...
를 사용하여 동일한 작업을 수행할 수 있습니다.
ES6
1 | function printf(format, ...params) { |
3.12 Spread operator
우리는 apply()
대신 전개 연산자를 사용합니다. 우리는 다시 ...
의 도움을 받습니다:
주의사항: 우리는 배열을 전달인자의 목록으로 바꾸기 위해
apply()
를 사용합니다. 예를 들어,Math.max()
는 전달인자의 목록을 취하지만 배열인 경우apply
를 사용하여 동작하게 합니다.
먼저 보았듯이, apply
를 사용해 전달인자의 목록을 배열로 넘겼습니다.
ES5
1 | Math.max.apply(Math, [2,100,1,6,43]) // 100 |
ES6에서는 전개 연산자를 사용할 수 있습니다.
ES6
1 | Math.max(...[2,100,1,6,43]) // 100 |
또한 우리는 concat
대신 전개 연산자를 사용할 수 있습니다.
ES5
1 | var array1 = [2,100,1,6,43]; |
ES6에서 전개 연산자를 사용해 중첩된 배열을 병합 할 수 있습니다:
ES6
1 | const array1 = [2,100,1,6,43]; |
4. Conclusion
자바스크립트는 많은 변화를 겪었습니다. 이 글은 모든 자바스크립트 개발자가 알아야 할 중심 기능을 다뤘습니다. 또한 코드를 보다 간결하고 쉽게 이해할 수 있는 모범 사례를 제공했습니다.
만약 꼭 알아야할 다른 기능이 있다면 아래 댓글로 알려주세요. 저는 이 글을 업데이트할 것 입니다.
5. 역자 후기
매번 ES6를 공부 해야지 생각만 하다가 좋은 글을 발견하여 읽게되었다.
읽는 도중에 영어가 막혀서 한글로 번역해서 보고 싶다고 생각했다.
그것이 나의 첫 번역이었다.
역시 영어가 서툰 나에게는 글 내용 자체를 이해하기도 힘들었다.
그래서 번역을 할 때 단어가 의미하는 것이 정확이 무엇인지 조사하고 그에 맞는 적절한 단어를 선택할 수 밖에 없었는데, 그 점이 오히려 나의 이해를 도운것 같다.