본문 바로가기

Language/Javascript

[개념] ES6이란?

ES6이란?
  • 자바스크립트 표준 사양인 ECMAScript의 6번째 버전
    기존 자바스크립트의 단점을 보완하고 코드 작성의 생산성과 가독성을 크게 향상시킨 표준
  • ECMAScript 2015로 알려져 있으며 ES6으로 불림
  • 2009년에 표준화된 ES5 이후 2015년에 자바스크립트의 대규모 업데이트가 이루어진 것이 ES6
  • ECMAScript는 ES6이후 매년 새로운 버전을 발표하였으며 버전 이름은 발표 연도로 정해지지만, ES6 이후의 버전들은 ES6+로 불림
    Ex) ES7(2016), ES8(2017), ES9(2018), ES10(2019), ES11(2020), ES12(2021), ES13(2022)

 

ES6 문법

const와 let
  • ES6에서 새로 등장한 키워드
  • const상수 선언이기 때문에 값의 재할당 불가능
    → Java의 final과 유사하지만 final은 변수 뿐만 아니라 클래스, 메서드에도 사용 되어 상속, 오버라이딩 제한 등 더 광범위한 의미를 가짐
  • const는 HTML 요소를 선택할 때 사용하는 것은 매우 좋은 관례
    → 단, 참조 변경이 필요할 경우 let 사용
// ES5
var MyBtn = document.getElementById('mybtn');

// ES6
const MyBtn = document.getElementById('mybtn');
  • let은 const와 달리 값의 재할당 가능
    → 즉, 변경 가능한 변수가 생성 됨
  • 중복 선언이 불가하여 동일한 이름의 변수를 같은 스코프 내에서 재 선언 불가
let name = '철수';
name = '영희';
console.log(name);
// 출력 => 영희

let a = 10;
let a = 20; // SyntaxError: Identifier 'a' has already been declared
  • var는 선언 위치와 상관 없이 스코프의 최상단으로 끌어올려지는 현상(호이스팅) 때문에 코드의 예측 가능성을 낮춤
  • const와 let은 블록스코프를 가지며, 선언 이전에 사용할 경우 ReferenceError가 발생하여 var 보다 안전
호이스팅(hoisting) 이란?
  • JavaScript 엔진이 실행 전에 변수와 함수 선언을 스코프의 최상단으로 끌어올리는 동작을 의미
  • 이로 인해 코드에서 변수를 선언하기 전에 참조해도 오류가 발생하지 않는 상황이 발생할 수 있음
console.log(a); // undefined (호이스팅 발생)
var a = 10;
console.log(a); // 10
  • 위의 코드는 JavaScript 엔진이 var를 호이스팅하여 아래와 같이 실행
var a;        // 선언만 끌어올림
console.log(a); // undefined
a = 10;       // 할당은 원래 위치에서 수행
console.log(a); // 10
  • 하지만, let과 const는 호이스팅 되더라도 TDZ(Temporal Dead Zone)에 걸려 선언 전에 접근 시 ReferenceError 발생
    TDZ는 let과 const의 변수 선언 이전에 해당 변수를 참조하지 못하도록 보호하는 메커니즘

    → TDZ는 JavaScript 코드의 안정성과 예측 가능성을 높임
console.log(a); // ReferenceError
let a = 10;

화살표 함수 (Arrow Functions)
  • 코드의 가독성을 높이고 this 바인딩 문제를 해결하는 데 유용
  • 화살표 함수를 map과 filter, reduce 등 내장 함수와 함께 사용 가능
  • Java의 람다 표현식과 유사
구문 간결화
// ES5
function myFunc(name) {
	return '안녕' + name;
}
console.log(myFunc('영희')); // 안녕 영희

// ES6 화살표 함수
const myFunc = (name) => {
	return `안녕 ${name}`;
}
console.log(myFunc('영희')); // 안녕 영희

// 중괄호와 return 또한 생략 가능
const myFunc = (name) => `안녕 ${name}`;
console.log(myFunc('영희')); // 안녕 영희
this 바인딩
  • 화살표 함수는 자신만의 this를 가지지 않는 대신 선언된 위치의 this를 상속
  • 일반 함수에서의 this는 호출되는 객체에 따라 달라지지만 화살표 함수는 이를 고정
const obj = {
    value: 10,
    regularFunction: function () {
        console.log(this.value); // 10 (obj를 가리킴)
    },
    arrowFunction: () => {
        console.log(this.value); // undefined (전역 `this`를 가리킴)
    },
};
obj.regularFunction();
obj.arrowFunction();

 

익명 함수로 사용
  • 화살표 함수는 항상 익명
  • 함수 이름이 필요하지 않은 곳(콜백 함수 등)에서 자주 사용
// ES5
const myArrary = ['진수', '영철', '영희', 5];
let arr1 = myArrary.map(function(item) {
	return item;
});
console.log(arr1); // 출력 => (4) ["진수", "영철", "영희", 5]

// ES6
let arr2 = myArrary.map((item) => item);
console.log(arr2); // 출력 => (4) ["진수", "영철", "영희", 5]

const numbers = [1, 2, 3];
const squares = numbers.map(num => num * num);
console.log(squares); // [1, 4, 9]
매개변수가 하나일 때 괄호 생략 가능
const square = x => x * x; // 매개변수가 하나라면 괄호 생략 가능
중괄호 사용 시 return 필요
  • 여러 줄로 구성된 함수는 중괄호를 사용하고 명시적으로 return을 반드시 사용
const multiply = (a, b) => {
    const result = a * b;
    return result;
};
화살표 함수의 한계
this를 가지지 않음
  • 객체 메서드로 사용할 때 화살표 함수는 적합하지 않음
const obj = {
    value: 10,
    method: () => console.log(this.value),
};
obj.method(); // undefined (this가 전역 객체를 가리킴)
arguments 객체를 가지지 않음
  • 화살표 함수는 arguments 객체를 사용할 수 없는 대신, ...rest 문법을 사용
const func = (...args) => args;
console.log(func(1, 2, 3)); // [1, 2, 3]
생성자로 사용할 수 없음
  • 화살표 함수는 new 키워드로 호출할 수 없음
const Person = (name) => {
    this.name = name;
};
const john = new Person('John'); // TypeError: Person is not a constructor

템플릿 리터럴 (Template Literals)
  • 기존 ES5에서 문자열과 변수를 연결하기 위해서작은 따옴표( ' ) 혹은 큰 따옴표( " )와 더하기 연산자(+)를 사용하여 표현
  • ES6에서는 백틱(`)을 사용하여 더하기 연산자(+) 없이 더 간결하고 직관적으로 표현 가능
// ES5
const name = '철수';
const age = 25;
const message = '안녕하세요, 제 이름은 ' + name + '이고, 나이는 ' + age + '살입니다.';
console.log(message); // 안녕하세요, 제 이름은 철수이고, 나이는 25살입니다.

// ES6 템플릿 리터럴
const name = '철수';
const age = 25;
const message = `안녕하세요, 제 이름은 ${name}이고, 나이는 ${age}살입니다.`;
console.log(message); // 안녕하세요, 제 이름은 철수이고, 나이는 25살입니다.
  • ${} 구문을 사용하여 문자열 안에 변수 삽입이 가능할 뿐만 아니라 표현식도 사용 가능
    JSP의 EL 표현식( ${} )과 유사
const x = 10;
const y = 20;
console.log(`x + y = ${x + y}`); // 출력: x + y = 30
  • 템플릿 리터럴을 사용하면 줄바꿈(\n) 없이여러 줄 문자열을 작성 가능
const multiline = `이것은
여러 줄로 작성된
문자열입니다.`;
console.log(multiline);
/* 출력:
이것은
여러 줄로 작성된
문자열입니다.
*/
  • 템플릿 리터럴을 가공하기 위해 함수와 함께 사용 가능
function tag(strings, ...values) {
    console.log(strings); // ["안녕하세요, 제 이름은 ", "이고, 나이는 ", "살입니다."]
    console.log(values); // ["철수", 25]
    return `${values[0]}님은 ${values[1]}살입니다.`;
}
const name = '철수';
const age = 25;
console.log(tag`안녕하세요, 제 이름은 ${name}이고, 나이는 ${age}살입니다.`); // 철수님은 25살입니다.

기본 매개 변수 (Default Parameters)
  • 함수에서 특정 파라미터에 값이 전달되지 않은 경우 해당 파라미터에 대한 기본값을 사용하는 방식
  • 함수 호출 시 매개변수를 명시적으로 전달하지 않아도 기본값을 사용해 예상치 못한 undefined 에러를 방지
  • 기본 매개 변수의 동작
    1. 파라미터가 넘어오지 않았을 경우
    2. undefined가 넘어왔을 경우
      단, null이 전달될 경우 기본값이 무시 되고 null 사용
// 기본 매개 변수를 사용하지 않은 경우
function greet(name) {
    return `Hello, ${name}!`;
}

console.log(greet());       // Hello, undefined!
console.log(greet('철수')); // Hello, 철수!

// 기본 매개 변수를 사용하는 경우
function greet(name = '익명') {
    return `Hello, ${name}!`;
}

console.log(greet());       // Hello, 익명!
console.log(greet('철수')); // Hello, 철수!
  • 기본 매개 변수와 undefined
function greet(name = '익명') {
    return `Hello, ${name}!`;
}

console.log(greet(undefined)); // Hello, 익명!
console.log(greet(null));      // Hello, null!
  • 동적 기본값 설정
    • 두 번째 매개 변수 b의 기본값이 첫 번째 매개 변수 a를 활용
function sum(a = 0, b = a * 2) {
    return a + b;
}

console.log(sum());       // 0 (a = 0, b = 0 * 2)
console.log(sum(5));      // 15 (a = 5, b = 5 * 2)
console.log(sum(5, 10));  // 15 (a = 5, b = 10)

배열 및 객체의 비구조화 (Array and Object destructing)
  • 배열이나 객체의 속성간단하고 가독성 좋게 변수로 추출하는 방법
  • 기존의 ES5 방식보다 훨씬 간결하고 효율적인 코드를 작성 가능
비구조화 할당
// 객체의 비구조화 할당 : 중괄호 사용
// ES5
const person = { 
    name: '철수', 
    age: 25, 
    city: '서울' 
};
const name = person.name;
const age = person.age;
const city = person.city;

console.log(name, age, city); // 철수 25 서울

// ES6
const person = {
    name: '철수', 
    age: 25, 
    city: '서울' 
};
const { name, age, city } = person;

console.log(name, age, city); // 철수 25 서울

 

// 배열의 비구조화 할당 : 대괄호 사용
// ES5
const colors = ['red', 'green', 'blue'];
const first = colors[0];
const second = colors[1];
const third = colors[2];

console.log(first, second, third); // red green blue

// ES6
const colors = ['red', 'green', 'blue'];
const [first, second, third] = colors;

console.log(first, second, third); // red green blue

 

기본값 설정
// 객체의 기본값
const person = { name: '철수', city: '서울' };
const { name, age = 20, city } = person;

console.log(name, age, city); // 철수 20 서울

// 배열의 기본값
const colors = ['red', 'green'];
const [first, second, third = 'blue'] = colors;

console.log(first, second, third); // red green blue

 

중첩 구조의 비구조화
// 객체의 중첩 구조
// ES5
var person = {
    name: '철수',
    address: {
        city: '서울',
        zip: '12345'
    }
};

// 중첩된 속성에 직접 접근
var name = person.name;
var city = person.address.city;
var zip = person.address.zip;

console.log(name); // 철수
console.log(city); // 서울
console.log(zip);  // 12345

// ES6
const person = {
    name: '철수', 
    address: {
        city: '서울', 
        zip: '12345' 
    }
};
const { name, address: { city, zip } } = person;

console.log(name, city, zip); // 철수 서울 12345
// 배열의 중첩 구조
// ES5
var colors = [
    ['red', 'green'],
    ['blue', 'yellow']
];

// 중첩된 배열의 요소에 인덱스를 이용해 접근
var first = colors[0][0];
var second = colors[0][1];
var third = colors[1][0];
var fourth = colors[1][1];

console.log(first);  // red
console.log(second); // green
console.log(third);  // blue
console.log(fourth); // yellow

// ES6
const colors = [
    ['red', 'green'], 
    ['blue', 'yellow']
];
const [ [first, second], [third, fourth] ] = colors;

console.log(first, second, third, fourth); // red green blue yellow

 

Rest 패턴
  • 나머지 값을 별도로 추출할 때도 유용
// 객체의 Rest 패턴
const person = {
    name: '철수', 
    age: 25, 
    city: '서울'
};
const { name, ...rest } = person;

console.log(name); // 철수
console.log(rest); // { age: 25, city: '서울' }

// 배열의 Rest 패턴
const colors = ['red', 'green', 'blue', 'yellow'];
const [first, ...rest] = colors;

console.log(first); // red
console.log(rest); // ['green', 'blue', 'yellow']

 

객체의 비구조화 할당 주의 사항
  • 객체의 속성명과 동일한 이름의 변수가 아니라면 undefined 반환
  • 반드시 속성의 이름과 동일하게 할당을 해야하며, 변수의 이름을 변경하려면 콜론( : )을 사용하여 변경 가능
// 속성명과 다를 경우 undefined
const person = {
    name: '철수', 
    age: 25, 
    city: '서울' 
};

let { username, age, city } = person;
console.log(username, age, city); // undefined 25 서울

// 콜론을 사용하여 변수명 변경
const person = {
    name: '철수', 
    age: 25, 
    city: '서울' 
};

let { name : username, age, city } = person;
console.log(username, age, city); // '철수' 25 '서울'

가져오기 및 내보내기 (Import and Export)
  • 모듈(module) 기능을 제공하는 ES6의 핵심 개념
  • JavaScript 코드의 재사용성과 관리성을 높이는 데 매우 유용

 

export
  • 다른 파일에서 사용할 수 있도록 함수, 변수, 클래스 등을 내보내는 역할
  • 두 가지 방식으로 사용
Named Export

 

  • 하나의 모듈에서 여러 개를 내보낼 수 있음
  • 각 항목은 고유한 이름 가져야 함

 

// math.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export const multiply = (a, b) => a * b;
export const divide = (a, b) => a / b;
Default Export

 

  • 모듈에서 하나의 기본 값만 내보낼 때 사용
  • export default는 이름 없이 내보낼 수 있음
// greet.js
export default function greet(name) {
    return `Hello, ${name}!`;
}

 

 

import
  • 다른 파일에서 내보낸 값을 가져오는 역할
  • 내보낸 방식에 따라 가져오는 방법이 다름
Named Import

 

  • 중괄호 {}를 사용해 원하는 항목을 가져옴
  • 이름이 정확히 일치해야 함
// main.js
import { add, subtract } from './math.js';

console.log(add(10, 5));       // 15
console.log(subtract(10, 5));  // 5

 

Default Import

 

  • 이름을 자유롭게 지정해서 가져올 수 있음
  • 중괄호 {} 없이 가져옴
// main.js
import greet from './greet.js';

console.log(greet('철수')); // Hello, 철수!

 

 

Named Import와 Default Import를 함께 사용
// utils.js
export const format = (text) => text.toUpperCase();
export default function log(message) {
    console.log(message);
}
// main.js
import log, { format } from './utils.js';

log(format('hello world')); // HELLO WORLD

 

Import All (*)
  • 모든 Named Export를 하나의 객체로 가져올 수 있음
// main.js
import * as math from './math.js';

console.log(math.add(10, 5));      // 15
console.log(math.multiply(10, 5)); // 50

 

모듈 경로
  • 상대 경로 : ./ 또는 ../로 시작 (./math.js)
  • 절대 경로 : node_modules 또는 프로젝트의 설정에 따라 경로 지정 가능 (lodash)

프로미스(Promiss)
  • ES6에서 추가된 비동기 코드를 사용하는 방식
  • 이에 대해서는 콜백 함수, 프로미스, async/await에 대해 정리해둔 링크를 참조

나머지 매개 변수 및 확산 연산자(Rest parameter and Spread operator)
  • 배열을 더 유연하고 간결하게 다룰 수 있도록 도와줌
나머지 매개변수 (Rest Parameter)
  • 함수의 매개변수로 전달된 나머지 값을 배열 형태로 받는 문법
  • ...을 사용하여 나머지 값을 하나의 배열로 처리
  • 함수의 매개변수 목록에서 마지막에 위치
function introduce(name, age, ...hobbies) {
    console.log(`이름: ${name}, 나이: ${age}`);
    console.log(`취미: ${hobbies.join(', ')}`);
}

introduce('철수', 25, '축구', '음악', '영화 감상');
// 출력:
// 이름: 철수, 나이: 25
// 취미: 축구, 음악, 영화 감상
  • 전달된 인자의 개수를 알 수 없을 때 유용
function sum(...numbers) {
    return numbers.reduce((acc, curr) => acc + curr, 0);
}

console.log(sum(1, 2, 3, 4, 5)); // 15

 

확산 연산자 (Spread Operator)
  • 확산 연산자는 배열 개별 요소로 분리하거나, 복사 및 결합하는 데 사용
  • ...을 사용하며, 배열, 함수 호출 등 여러 상황에서 활용
// 배열 복사
const arr1 = [1, 2, 3];
const arr2 = [...arr1]; // arr1의 복사본 생성
console.log(arr2); // [1, 2, 3]

// 배열 결합
const arr1 = [1, 2];
const arr2 = [3, 4];
const combined = [...arr1, ...arr2];
console.log(combined); // [1, 2, 3, 4]

// 함수 호출
const numbers = [10, 20, 30];
console.log(Math.max(...numbers)); // 30
차이점
특징 나머지 매개 변수(Rest) 확산 연산자(Spread)
역할 나머지 값을 배열로 수집 배열을 개별 요소로 분리
주요 사용처 함수의 매개 변수 배열, 함수 호출 등
문법 위치 함수 정의 시 ...변수명 배열, 함수 호출 앞 ...

클래스(Classes)
  • 객체를 생성하기 위한 템플릿 역할
  • 이전에는 생성자 함수와 프로토타입을 사용하여 객체를 생성하고 상속을 구현했으나, ES6에서 클래스 문법이 도입되면서 더 간결하고 직관적인 방식으로 객체 지향 프로그래밍이 가능해짐
class Person {
    // 생성자 함수 (객체 초기화)
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    // 메서드 정의
    greet() {
        console.log(`안녕하세요, 저는 ${this.name}이고, ${this.age}살입니다.`);
    }
}

const person1 = new Person('철수', 25);
person1.greet(); // 안녕하세요, 저는 철수이고, 25살입니다.
클래스의 주요 특징
생성자(Constructor)

 

  • 클래스에서 객체를 초기화하기 위해 사용하는 메서드
  • 클래스 안에 한 번만 정의 가능
  • 객체가 생성될 때 자동으로 호출
class Car {
    constructor(brand, model) {
        this.brand = brand;
        this.model = model;
    }
}

const myCar = new Car('Hyundai', 'Sonata');
console.log(myCar);

 

메서드 (Method)

 

  • 클래스 안에서 정의된 함수
  • 생성된 객체를 통해 호출 가능
class Animal {
    constructor(name) {
        this.name = name;
    }

    speak() {
        console.log(`${this.name}가 소리를 냅니다.`);
    }
}

const dog = new Animal('강아지');
dog.speak(); // 강아지가 소리를 냅니다.

 

 

정적 메서드 (Static Method)

 

  • 클래스 자체에서 호출할 수 있는 메서드
  • 객체가 아니라 클래스 이름으로 호출
  • static 키워드로 선언
class MathUtil {
    static add(a, b) {
        return a + b;
    }
}

console.log(MathUtil.add(5, 3)); // 8

 

 

상속 (Inheritance)

 

  • extends 키워드를 사용하여 클래스를 확장 가능
  • 부모 클래스의 속성과 메서드를 자식 클래스에서 사용 가능
  • super를 사용하여 부모 클래스의 생성자나 메서드를 호출 가능
class Vehicle {
    constructor(type) {
        this.type = type;
    }

    describe() {
        console.log(`이것은 ${this.type}입니다.`);
    }
}

class Bike extends Vehicle {
    constructor(type, brand) {
        super(type); // 부모 클래스의 생성자 호출
        this.brand = brand;
    }

    showBrand() {
        console.log(`브랜드: ${this.brand}`);
    }
}

const myBike = new Bike('자전거', '삼천리');
myBike.describe(); // 이것은 자전거입니다.
myBike.showBrand(); // 브랜드: 삼천리

 

 

클래스와 객체 리터럴의 차이
특징 클래스 (Class) 객체 리터럴 (Object Literal)
생성 방식 new 키워드를 사용해 인스턴스 생성 중괄호 {}로 생성
메서드 정의 클래스 내부에 정의 객체에 직접 정의
상속 extends와 super로 구현 Object.create() 또는 프로토타입 사용

참고 자료

https://velog.io/@kimhscom/JavaScript-%EC%9E%90%EC%A3%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-ES6-%EB%AC%B8%EB%B2%95-%EC%A0%95%EB%A6%AC

 

[JavaScript] 자주 사용하는 ES6 문법 정리

들어가기 전에 Node.js와 React관련 프로젝트를 진행하면서 기존에 배워왔던 jQuery를 활용한 ES5 문법으로 JavaScript 코드를 작성하였지만 이제는 최신 트렌드에 맞게 ES6 문법으로 JavaScript 코드를 작성

velog.io