본문 바로가기
Front-End/JavaScript

ES6 문법

by 연제원 2021. 1. 9.

공부를 하면서 나도 모르게 편하게 사용되는 것들이, 알고보니 ES6이 도입되고나서부터 사용되는 것들이 정말 많았다. 아무 생각없이 사용하던 것들에 대해 복습 겸 공부 겸 개념정리 겸 es6 문법에 대해 정리를 해보려고 한다!

✅ ES6이란?

ECMAScript 2015로도 알려져 있는 ECMAScript 6는 ECMAScript 표준의 가장 최신 버전이다. ES6는 새로운 언어 기능이 포함된 주요 업데이트이며, 2009년도에 표준화된 ES5 이후로 언어 기능에 대한 첫 업데이트라고 한다.

 

여기서

ECMAScript란?

자바스크립트 언어의 표준 규격을 의미

 

ES6은 새로운 기능들을 많이 추가했는데 이중에 자주 배웠던 것을 우선적으로 알아볼 것이다.


ECMAScript 6 에 추가된 기능


✅ let + const

블록 유효 범위 { } 를 갖는 새로운 변수 선언 방법을 지원한다. 기존에 있던 var는 Function Scope를 따랐다. 하지만 let, const는 Block Scope를 따른다.

 

var, let, const의 차이점

  var let const
Scope(유효 범위) 함수(function) 블록 { } (Block) 블록 { } (Block)
재 선언
재 할당
변수 선언 시 초기 값 주지 않아도 됨 주지 않아도 됨 반드시 필요

var은 쓰지 않는 것을 추천한다고 한다.


✅ Arrows

Arrows(화살표) 함수는 => 문법을 사용하는 축약형 함수다. 추상화를 위한 선언형 프로그래밍을 할 때, 아주 유용할 것 같다. 특이한 점은 화살표 함수에서 this는 함수 자신을 호출하는 객체(dynamic this)가 아닌 화살표 함수의 상위 스코프(lexical this)를 호출한다.

(this란 간단히 말해 메소드를 호출한 객체가 저장되어 있는 속성이다. 자세한 공부가 필요할 것 같다!)

 

기본 구문

(param1, param2, …, paramN) => { statements }
(param1, param2, …, paramN) => expression
// 다음과 동일함:  => { return expression; }

// 매개변수가 하나뿐인 경우 괄호는 선택사항:
(singleParam) => { statements }
singleParam => { statements }

// 매개변수가 없는 함수는 괄호가 필요:
() => { statements }

예시

const func1 = function() { const num = 10; };
const func1 = () => { const num = 10; }; // function 키워드 생략 가능

const func2 = function(num) { 
  for(let i = 0; i < 10; i++) { num++; }
  return num;
};
const func2 = num => {					// 함수의 매개변수에 괄호 생략 가능
  for(let i = 0; i < 10; i++) { num++; }
  return num;
};

const func3 = function (num) { return `입력된 숫자는 ${num}입니다.`; };
const func3 = num => `입력된 숫자는 ${num}입니다.`; // 중괄호와 return 문 생략 가능

✅ Classes

자세한 정리

Class는 객체를 생성하기 위한 템플릿이다.

다른 객체지향 언어(C++, C#, Java, Python, Ruby 등…)에서 사용되는 Class 문법과는 달리 JavaScript에는 class라는 개념이 없다. class가 없기 때문에 기본적으로 class 기반의 상속도 불가능하다. 대신 다른 언어에는 존재하지 않는 프로토타입(prototype)이라는 것이 존재한다. JavaScript는 이 prototype을 기반으로 상속을 흉내내도록 구현해 사용한다고 한다. (proto type 이해하기)

 

class는 함수와 같이(함수 표현식, 함수 선언식) class 선언class 표현식 두 가지 방법으로 정의가 가능하다.

 

class 선언

함수 선언과 달리 클래스 선언은 호이스팅이 일어나지 않는다. 따라서 먼저 선언을 해야한다. 그렇지 않으면 ReferenceError가 발생한다.

class People {
    constructor(name) {
        this.name = name;
    }

    say() {
        console.log('My name is ' + this.name);
    }
}
const p = new Rectangle(); // ReferenceError

class Rectangle {}

 

class 표현식

클래스 표현식은 이름을 가질 수도 있고, 갖지 않을 수도 있다. 이름을 가진 class 표현식의 이름은 클래스 body의 local scope에 한해 유효하다. (하지만, 클래스의 (인스턴스 이름이 아닌) name 속성을 통해 찾을 수 있다).

클래스 표현식은 클래스 선언과 동일하게 호이스팅 제한이 적용된다.

// unnamed
let Rectangle = class {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(Rectangle.name);
// 출력: "Rectangle"

// named
let Rectangle = class Rectangle2 {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};
console.log(Rectangle.name);
// 출력: "Rectangle2"

✅ enhanced object literals

ES6에서는 객체 리터럴 프로퍼티 기능을 확장하여 더욱 간편하고 동적인 객체 생성 기능을 제공한다.

 

# 1. 프로퍼티 축약 표현

ES5에서 객체 리터럴의 프로퍼티는 프로퍼티 이름과 프로퍼티 값으로 구성된다. 프로퍼티의 값은 변수에 할당된 값일 수도 있다.

ES6에서는 프로퍼티 값으로 변수를 사용하는 경우, 프로퍼티 이름을 생략(Property shorthand)할 수 있다. 이때 프로퍼티 이름은 변수의 이름으로 자동 생성된다.

// ES5
var x = 1, y = 2;
var obj = {
  x: x,
  y: y
};
console.log(obj); // { x: 1, y: 2 }

// ES6
let x = 1, y = 2;
const obj = { x, y };
console.log(obj); // { x: 1, y: 2 }

 

# 2. 프로퍼티 키 동적 생성

문자열 또는 문자열로 변환 가능한 값을 반환하는 표현식을 사용해 프로퍼티 키를 동적으로 생성할 수 있다. 단, 프로퍼티 키로 사용할 표현식을 대괄호([…])로 묶어야 한다. 이를 계산된 프로퍼티 이름(Computed property name)이라 한다.

ES5에서 프로퍼티 키를 동적으로 생성하려면 객체 리터럴 외부에서 대괄호([…]) 표기법을 사용해야 한다.

ES6에서는 객체 리터럴 내부에서도 프로퍼티 키를 동적으로 생성할 수 있다.

// ES5
var prefix = 'prop';
var i = 0;
var obj = {};
obj[prefix + '-' + ++i] = i;
obj[prefix + '-' + ++i] = i;
obj[prefix + '-' + ++i] = i;
console.log(obj); // {prop-1: 1, prop-2: 2, prop-3: 3}

// ES6
const prefix = 'prop';
let i = 0;
const obj = {
  [`${prefix}-${++i}`]: i,
  [`${prefix}-${++i}`]: i,
  [`${prefix}-${++i}`]: i
};
console.log(obj); // {prop-1: 1, prop-2: 2, prop-3: 3}

 

# 3. 메소드 축약 표현

ES5에서 메소드를 선언하려면 프로퍼티 값으로 함수 선언식을 할당한다.

ES6에서는 메소드를 선언할 때, function 키워드를 생략한 축약 표현을 사용할 수 있다.

// ES5
var obj = {
  name: 'Jewon',
  sayHi: function() {
    console.log('Hi! ' + this.name);
  }
};
obj.sayHi(); // Hi! Jewon

// ES6
const obj = {
  name: 'Jewon',
  // 메소드 축약 표현
  sayHi() {
    console.log('Hi! ' + this.name);
  }
};
obj.sayHi(); // Hi! Jewon

# 4. __proto__프로퍼티에 의한 상속 

ES5에서 객체 리터럴을 상속하기 위해서는 Object.create() 함수를 사용한다. 이를 프로토타입 패턴 상속이라 한다.

ES6에서는 객체 리터럴 내부에서 __proto__프로퍼티를 직접 설정할 수 있다. 이것은 객체 리터럴에 의해 생성된 객체의 __proto__ 프로퍼티에 다른 객체를 직접 바인딩하여 상속을 표현할 수 있음을 의미한다.

// ES5
var parent = {
  name: 'parent',
  sayHi: function() {
    console.log('Hi! ' + this.name);
  }
};
// 프로토타입 패턴 상속
var child = Object.create(parent);
child.name = 'child';
parent.sayHi(); // Hi! parent
child.sayHi();  // Hi! child

// ES6
const parent = {
  name: 'parent',
  sayHi() {
    console.log('Hi! ' + this.name);
  }
};

const child = {
  // child 객체의 프로토타입 객체에 parent 객체를 바인딩하여 상속을 구현한다.
  __proto__: parent,
  name: 'child'
};

parent.sayHi(); // Hi! parent
child.sayHi();  // Hi! child

✅ template strings

Template Strings(ES6 부터는 Template literals라 부름)는 문법적으로 더 편하게 string을 생성할 수 있게 한다. 

기존에 문자열을 사용할 땐 작은 따옴표(' '), 큰 따옴표(" ")를 썼다면, template strings는 백틱 (` `)을 사용한다.

추가로, 표현식도 안에 넣을 수 있다! ${표현식}

백틱안에서 띄어쓰기도 인식하니 매우 편하다..!

// 일반적인 형태의 string 표기법
    
// 1. 일반적인 문자열 선언
const greeting = "Hello"
// 2. 문자열과 표현식의 결합
const lastName = "연";
const firstName = "제원";
    
const MyInfo = "성은" + lastName + "이고, 이름은" + firstName +"입니다."
// "성은 연이고, 이름은 제원입니다."

// ES6의 Template Literal 사용법
    
// 1. 위와 동일, 백틱 사용
const greeting = `Hello`  
// 2. ''(백틱) 과 ${표현식} 사용
const lastName = "연";
const firstName = "제원";
   
const MyInfo = `성은 ${lastName}이고, 이름은 ${firstName}입니다.`

✅ destructuring (구조 분해 할당)

destructuring 구문은 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JavaScript 표현식이다. 배열 또는 객체 리터럴에서 필요한 값만을 추출하여 변수에 할당하거나 반환할 때 유용하다.

 

# 1. 배열 디스트럭처링

ES6의 배열 디스트럭처링은 배열의 각 요소를 배열로부터 추출하여 변수 리스트에 할당한다. 이때 추출/할당 기준은 배열의 인덱스이다. 배열 디스트럭처링을 위해서는 할당 연산자 왼쪽에 배열 형태의 변수 리스트가 필요하다.

// ES6 Destructuring
const arr = [1, 2, 3];

// 배열의 인덱스를 기준으로 배열로부터 요소를 추출하여 변수에 할당
// 변수 one, two, three가 선언되고 arr(initializer(초기화자))가 Destructuring(비구조화, 파괴)되어 할당된다.
const [one, two, three] = arr;
// 디스트럭처링을 사용할 때는 반드시 initializer(초기화자)를 할당해야 한다.
// const [one, two, three]; // SyntaxError: Missing initializer in destructuring declaration

console.log(one, two, three); // 1 2 3

또한, ES6의 배열 디스트럭처링은 배열에서 필요한 요소만 추출하여 변수에 할당하고 싶은 경우에 유용하다. Date 객체에서 년도, 월, 일을 추출하는 예제를 보며 이해해보자.

const today = new Date(); // Sat Jan 09 2021 22:41:07 GMT+0900 (대한민국 표준시) {}
const formattedDate = today.toISOString().substring(0, 10); // "2021-01-09"
const [year, month, day] = formattedDate.split('-');
console.log([year, month, day]); // [ '2021', '01', '09' ]

 

# 2. 객체 디스트럭처링

ES6의 객체 디스트럭처링은 객체의 각 프로퍼티를 객체로부터 추출하여 변수 리스트에 할당한다. 이때 할당 기준은 프로퍼티 이름(키)이다.

// ES6 Destructuring
const obj = { firstName: 'Jewon', lastName: 'Yeon' };

// 프로퍼티 키를 기준으로 디스트럭처링 할당이 이루어진다. 순서는 의미가 없다.
// 변수 lastName, firstName가 선언되고 obj(initializer(초기화자))가 Destructuring(비구조화, 파괴)되어 할당된다.
const { lastName, firstName } = obj;

console.log(firstName, lastName); // Jewon Yeon

객체 디스트럭처링을 위해서는 할당 연산자 왼쪽에 객체 형태의 변수 리스트가 필요하다.

// 프로퍼티 키가 prop1인 프로퍼티의 값을 변수 p1에 할당
// 프로퍼티 키가 prop2인 프로퍼티의 값을 변수 p2에 할당
const { prop1: p1, prop2: p2 } = { prop1: 'a', prop2: 'b' };
console.log(p1, p2); // 'a' 'b'
console.log({ prop1: p1, prop2: p2 }); // { prop1: 'a', prop2: 'b' }

// 아래는 위의 축약형이다
const { prop1, prop2 } = { prop1: 'a', prop2: 'b' };
console.log({ prop1, prop2 }); // { prop1: 'a', prop2: 'b' }

// default value
const { prop1, prop2, prop3 = 'c' } = { prop1: 'a', prop2: 'b' };
console.log({ prop1, prop2, prop3 }); // { prop1: 'a', prop2: 'b', prop3: 'c' }

객체 디스트럭처링은 객체에서 프로퍼티 이름(키)으로 필요한 프로퍼티 값만을 추출할 수 있다. Array.prototype.filter 메소드의 콜백 함수는 대상 배열(todos)을 순회하며 첫 번째 인자로 대상 배열의 요소를 받는다. 이때 필요한 프로퍼티(completed 프로퍼티)만을 추출할 수 있다.

const todos = [
  { id: 1, content: 'HTML', completed: true },
  { id: 2, content: 'CSS', completed: false },
  { id: 3, content: 'JS', completed: false }
];

// todos 배열의 요소인 객체로부터 completed 프로퍼티만을 추출한다.
const completedTodos = todos.filter(({ completed }) => completed);
console.log(completedTodos); // [ { id: 1, content: 'HTML', completed: true } ]

추가로, 중첩 객체의 경우

const person = {
  name: 'Jewon',
  address: {
    city: 'Ulsan'
  }
};

const { address: { city } } = person;
console.log(city); // 'Ulsan'

 

요약

Destructuring 배열 객체
추출/할당 기준 인덱스 프로퍼티 이름 (key)
공통점 할당 연산자( = ) 왼쪽에 (배열 형태 / 객체 형태)의 변수 리스트가 필요

✅ default + rest + spread

공부를 하면서 rest parameter와 spread operator(syntax)가 정말 많이 헷갈렸다. 둘 다 ... 을 사용하는 데 뭐가 다른가?

간단하게 말하자면,  Rest 파라미터는 Spread 문법을 사용하여 파라미터를 정의한 것을 의미한다. 또한 많은 차이점이 있다. 자세하게 공부해보자.

#1 . 매개변수 기본 값 (default parameter value)

매개변수 기본값을 사용하여 함수 내에서 수행하던 인수 체크 및 초기화를 간소화할 수 있다. 매개변수 기본값은 매개변수에 인수를 전달하지 않았을 경우에만 유효하다. 매개변수 기본값은 함수 정의 시 선언한 매개변수 개수를 나타내는 함수 객체의 length 프로퍼티와 arguments 객체에 영향을 주지 않는다.

 

// default값 설정
function sum(x = 0, y = 0) {
  return x + y;
}

console.log(sum(1));    // 1 (y에 인수전달이 안되었으므로 초기값 0을 대입)
console.log(sum(1, 2)); // 3


// length에 영향을 미치지 않는다.
function foo(x, y = 0) {
  console.log(arguments);
}

console.log(foo.length); // 1

sum(1);    // Arguments { '0': 1 }
sum(1, 2); // Arguments { '0': 1, '1': 2 }

 

#2. Rest Parameter

Rest 파라미터(Rest Parameter, 나머지 매개변수)는 매개변수 이름 앞에 세개의 점 ... 을 붙여서 정의한 매개변수를 의미한다. Rest 파라미터는 함수에 전달된 인수들의 목록을 배열로 전달받는다. 그 다음, 함수에 전달된 인수들은 순차적으로 파라미터와 Rest 파라미터에 할당된다. 중요한 점은 rest라는 이름에서 알 수 있듯이 반드시 마지막에 사용해야 한다. Rest 파라미터 역시(default와 같이) 함수 정의 시 선언한 매개변수 개수를 나타내는 함수 객체의 length 프로퍼티에 영향을 주지 않는다.

// 예시 1
function foo(...rest) {
  console.log(Array.isArray(rest)); // true
  console.log(rest); // [ 1, 2, 3, 4, 5 ]
}

foo(1, 2, 3, 4, 5);

// 예시 2
function foo(param, ...rest) {
  console.log(param); // 1
  console.log(rest);  // [ 2, 3, 4, 5 ]
}

foo(1, 2, 3, 4, 5);

// 마지막에 사용하지 않으면 Error발생
function foo( ...rest, param1, param2) { }

foo(1, 2, 3, 4, 5);
// SyntaxError: Rest parameter must be last formal parameter

 

 

#3. Spread 문법

Spread 문법(Spread Syntax, ...)는 대상을 개별 요소로 분리한다. Spread 문법의 대상은 이터러블이어야 한다.


✅ iterators + for ... of


✅ generators


✅ unicode


✅ modules


✅ module loaders


✅ map + set + weakmap + weakset


✅ proxies


✅ symbols


✅ subclassable built-ins


✅ promises


✅ math + number + string + array + object APIs


✅ binary and octal literals


✅ reflect api


✅ tail calls

'Front-End > JavaScript' 카테고리의 다른 글

프로토타입(Prototype) 이해하기(1)  (0) 2021.01.15
this  (0) 2021.01.12
명령형 VS 선언형 프로그래밍  (0) 2021.01.08
고차 함수_3 (feat. 추상화)  (0) 2021.01.08
고차 함수_2 (feat. 내장 고차 함수 filter, map)  (0) 2021.01.08

댓글