728x90
ES6+
- ES6+는 이전 포스팅에서도 언급했지만, ECMAScript2015(ES6) 이후의 버전들을 묶어서 부르는 것
ES7 (ECMAScript 2016)
Array.prototype.includes
- 배열에 특정 요소가 포함되어 있는 지 확인하는 메서드
[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false
지수 연산자
- Math.pow()를 더 간결한 문법으로 제공
2 ** 3; // 8 == (2^3) == Math.pow(2,3)
ES7 (ECMAScript 2016)
async와 await
- 비동기 프로그래밍을 더 간결하게 처리할 수 있는 키워드
async function fetchData() {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
}
Object.entries
- 객체를 키-값 쌍의 배열로 변환 [ 객체 → 배열 ]
- 반환 형태 : [ [key1, value1], [key2, value2], ... ]
- 주요 용도
- 객체를 배열 형태로 변환해 반복(iteration)을 쉽게 처리
- Map 자료구조와 상호 변환 가능
const obj = { a: 1, b: 2, c: 3 };
console.log(Object.entries(obj));
// [ ['a', 1], ['b', 2], ['c', 3] ]
// 반복문에서 사용
for (const [key, value] of Object.entries(obj)) {
console.log(`${key}: ${value}`);
}
// a: 1
// b: 2
// c: 3
Object.values
- 객체의 값만 배열로 변환 [객체의 값 → 배열]
- 반환 형태 : [value1, value2, value3, ...]
- 주요 용도
- 객체의 값만 필요한 경우에 유용
const obj = { a: 1, b: 2, c: 3 };
console.log(Object.values(obj));
// [1, 2, 3]
문자열 패딩 (padStart, padEnd)
- 문자열의 시작 또는 끝에 지정된 길이만큼 문자를 추가
console.log('123'.padStart(5, '0')); // '00123'
console.log('123'.padEnd(5, '0')); // '12300'
console.log('12345'.padStart(5, '0')); // 12345
ECMAScript 2018 (ES9)
Rest/Spread 연산자 (객체에서의 지원)
- 객체에 대한 분해와 병합을 더 간단하게 처리
- ES6에서의 Rest와 Spread 연산자는 배열에서만 지원이 되었었지만, ES9부터는 객체에도 적용이 가능하게 됨
// 객체 복사
const obj1 = {
name: '철수',
age: 25
};
const obj2 = { ...obj1 }; // obj1의 복사본 생성
console.log(obj2); // { name: '철수', age: 25 }
// 객체 결합
const obj1 = {
name: '철수'
};
const obj2 = {
age: 25
};
const combined = { ...obj1, ...obj2 };
console.log(combined); // { name: '철수', age: 25 }
// 객체 속성 덮어쓰기
const obj1 = {
name: '철수',
age: 20
};
const obj2 = {
age: 25,
city: '서울'
};
const merged = { ...obj1, ...obj2 };
console.log(merged); // { name: '철수', age: 25, city: '서울' }
ES6와 ES9의 차이점 비교
특징 | ES6의 Rest/Spread | ES9의 Rest/Spread |
도입된 버전 | ES6(2015) | ES9(2018) |
사용 대상 | 배열 (Rest = 매개변수 수집, Spread = 펼치기) |
객체 (Rest = 속성 제외, Spread = 복사/병합) |
Rest 연산자 용도 | 함수의 인자를 배열로 수집 | 객체의 일부 속성을 분리하거나 추출 |
Spread 연산자 용도 | 배열을 병합하거나 함수 호출 시 인자 전달 | 객체를 복사하거나 병합 |
비동기 반복문 (for await...of)
- 비동기 iterable 객체를 순회하는 반복문
async function* asyncGenerator() {
yield 'a';
yield 'b';
}
(async () => {
for await (const value of asyncGenerator()) {
console.log(value);
}
})();
정규 표현식 개선
- Named Capturing Groups (이름 있는 캡처 그룹)
- ES9 이전 방식
- 정규식의 캡처 그룹은 숫자 기반으로 참조했기 때문에 캡처된 값이 많아지면 코드의 가독성이 떨어졌음
- 캡처된 값은 배열 인덱스로만 접근 가능했으며 어떤 그룹이 어떤 데이터를 나타내는지 파악하기 어려웠음
- ES9 이후 방식
- 캡처 그룹에 이름을 붙일 수 있어, 결과를 객체 형태로 반환받아 가독성과 유지보수가 용이
- 그룹 이름은 ?<name> 형식으로 정의
- groups는 예약된 속성명
→ regex.exec() 메서드의 결과로 반환되는 match 객체에 자동으로 추가되며, 이름 있는 캡처 그룹에서 정의한 이름들(?<year>, ?<month>, ?<day>)이 groups 객체의 속성으로 들어감
// ES9 이전 방식
const regex = /(\d{4})-(\d{2})-(\d{2})/;
const match = regex.exec("2024-12-25");
console.log(match[0]); // 전체 매칭 결과: "2024-12-25"
console.log(match[1]); // 첫 번째 캡처 그룹: "2024"
console.log(match[2]); // 두 번째 캡처 그룹: "12"
console.log(match[3]); // 세 번째 캡처 그룹: "25"
// ES9 이후 방식
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
// const match = regex.exec("2024-12-25");
const { groups: { year, month, day } } = regex.exec('2024-12-25');
// console.log(match.groups.year); // "2024"
// console.log(match.groups.month); // "12"
// console.log(match.groups.day); // "25"
console.log(year, month, day); // 2024 12 25
- Lookbehind Assertions (후방 탐색 어설션)
- ES9 이전 방식
- 후방 탐색이 지원되지 않았음
- 문자열의 앞에 특정 패턴이 존재하는 경우를 확인하려면 우회 방법을 사용
→ 문자열을 순서를 반대로 뒤집고 lookahead를 사용하는 등의 복잡한 방식으로 처리
- ES9 이후 방식
- 후방 탐색을 통해 문자열 앞에 특정 패턴이 존재하는지 확인이 가능
- (?<=...) : 긍정 후방 탐색. 지정한 패턴이 앞에 있을 때만 매칭
- (?<!...) : 부정 후방 탐색. 지정한 패턴이 앞에 없을 때만 매칭
// ES9 이전 방식
const text = "price: $123";
const regex = /\$\d+/; // '$'로 시작하는 숫자
const match = regex.exec(text);
console.log(match[0]); // "$123"
// 특정 텍스트가 '$' 뒤에 오는지 확인하는 데 불편함이 많음
// ES9 이후 방식
// 긍정 후방 탐색
const regexPositive = /(?<=\$)\d+/; // '$' 뒤에 있는 숫자
const matchPositive = regexPositive.exec("price: $123"); // 123
console.log(matchPositive[0]); // "123"
// 부정 후방 탐색
const regexNegative = /(?<!\$)\d+/; // '$' 뒤에 오지 않는 숫자
const matchNegative = regexNegative.exec("price : $123"); // 23
console.log(matchNegative[0]); // "123"
ECMAScript 2019 (ES10)
Array.prototype.flat
- 중첩된 배열을 평평하게(1차원으로) 만듦
- 기본적으로 한 단계만 평탄화하지만, 옵션으로 평탄화 깊이를 지정 가능
- depth(선택) : 평탄화할 깊이를 나타내는 숫자 (기본값 1)
array.flat([depth]);
// 기본 사용
const arr = [1, 2, [3, 4, [5, 6]]];
console.log(arr.flat()); // [1, 2, 3, 4, [5, 6]]
// 여러 단계 평탄화
console.log(arr.flat(2)); // [1, 2, 3, 4, 5, 6]
// 무한 깊이 평탄화 (Infinity 사용)
const deeplyNested = [1, [2, [3, [4, [5]]]]];
console.log(deeplyNested.flat(Infinity)); // [1, 2, 3, 4, 5]
// 빈 항목 제거
const arrWithHoles = [1, 2, , 4, , , 7];
console.log(arrWithHoles.flat()); // [1, 2, 4, 7]
Array.prototype.flatMap
- map() 메서드로 배열을 변환한 뒤 결과를 평탄화
→ 즉, 한 번에 변환(mapping)과 평탄화(flattening)를 수행 - callback : 배열의 각 요소에 대해 실행할 함수
- thisArg(선택) : callback 함수 내에서 this로 사용할 값
array.flatMap(callback(currentValue[, index[, array]])[, thisArg]);
// map()과 flat()을 함께 사용한 경우
const arr = [1, 2, 3, 4];
console.log(arr.map(x => [x, x * 2]).flat()); // [1, 2, 2, 4, 3, 6, 4, 8]
// flatMap()으로 간단히 표현
console.log(arr.flatMap(x => [x, x * 2])); // [1, 2, 2, 4, 3, 6, 4, 8]
// 텍스트 쪼개기와 평탄화
const sentences = ["hello world", "flatMap is great"];
console.log(sentences.flatMap(sentence => sentence.split(" ")));
// ["hello", "world", "flatMap", "is", "great"]
// 빈 배열 처리
const nested = [1, 2, , 4];
console.log(nested.flatMap(x => (x ? [x] : []))); // [1, 2, 4]
flat과 flatMap의 차이점 비교
특징 | flat() | flatMap() |
기능 | 배열 평탄화 | 매핑 후 평탄화 |
평탄화 깊이 | 지정 가능 (기본값 : 1) | 항상 1단계 |
조합 여부 | flat()과 다른 메서드(map)를 조합해야 함 | 한 번에 변환 및 평탄화 수행 |
사용 사례 | 다중 배열 구조를 단순화 | 데이터 변환 후 평탄화 |
Object.fromEntries
- 키-값 쌍의 배열 또는 이터러블을 객체로 변환 [ 배열 → 객체 ]
- 입력 형태 : [ [key1, value1], [key2, value2], ... ] 또는 이와 유사한 이터러블
- 주요 용도
- Object.entries의 반대 작업
- 배열 데이터를 기반으로 객체를 동적으로 생성
const entries = [['a', 1], ['b', 2], ['c', 3]];
console.log(Object.fromEntries(entries));
// { a: 1, b: 2, c: 3 }
// Map -> Object 변환
const map = new Map([['a', 1], ['b', 2]]);
console.log(Object.fromEntries(map));
// { a: 1, b: 2 }
ES7의 Object.entries, Object.values와 ES10의 Object.fromEntries 차이점 정리
특징 | Object.entries (ES7) | Object.values (ES7) | Object.fromEntries (ES10) |
동작 | 객체 → 배열 (키-값 쌍) | 객체 → 배열 (값만) | 배열/이터러블 → 객체 |
반환 형태 | [ [key1, value1], [key2, values2], ... ] | [values1, values2, ...] | { key1 : value1, key2 : value2, ... } |
주요 용도 | 객체 반복 및 배열화 | 객체 값 추출 | 배열을 객체로 변환 |
응용 예시 | Map과 상호 변환, 정렬 등 | 합계 계산 등 | 동적으로 객체 생성 |
Object.entries + Object.fromEntries 응용
const obj = { b: 2, a: 1, c: 3 };
// 1. 객체를 배열로 변환 후 정렬
const sortedEntries = Object.entries(obj).sort((a, b) => a[0].localeCompare(b[0]));
console.log(sortedEntries);
// [ ['a', 1], ['b', 2], ['c', 3] ]
// 2. 배열을 객체로 변환
const sortedObj = Object.fromEntries(sortedEntries);
console.log(sortedObj);
// { a: 1, b: 2, c: 3 }
String.prototype.trimStart와 trimEnd
- 문자열 앞/뒤 공백 제거
' hello '.trimStart(); // 'hello '
' hello '.trimEnd(); // ' hello'
Optional Catch Binding
- catch에서 매개변수 생략 가능
try {
throw new Error('error');
} catch {
console.error('An error occurred.');
}
ECMAScript 2020 (ES11)
Optional Chaining (?.)
- 객체가 null/undefined인지 확인하며 안전하게 접근
const obj = { a: { b: 2 } };
console.log(obj?.a?.b); // 2
console.log(obj?.c?.d); // undefined
Nullish Coalescing (??)
- null 또는 undefined일 때만 기본값 사용하는 논리 연산자
- Nullish Coalsescing Operator 작동 방식
- A ?? B는 A가 null 또는 undefined인 경우 B를 반환
- 만약 A가 null도 아니고 undefined도 아니면, A를 그대로 반환
- 논리 연산자 ||와 유사하지만 false, 0, '' (falsy 값)은 기본값으로 간주하지 않는 점에서 차이를 보임
const value1 = null ?? 'default'; // A가 null이므로 B를 반환
console.log(value1); // "default"
const value2 = undefined ?? 'default'; // A가 undefined이므로 B를 반환
console.log(value2); // "default"
const value3 = 0 ?? 'default'; // A가 0 (falsy 값)지만 nullish 값이 아니므로 A를 반환
console.log(value3); // 0
const value4 = '' ?? 'default'; // A가 빈 문자열(falsy 값)지만 nullish 값이 아니므로 A를 반환
console.log(value4); // ""
- 기본값 설정
function greet(name) {
// name이 nullish 값(null, undefined)일 때만 기본값 사용
const userName = name ?? 'Guest';
console.log(`Hello, ${userName}!`);
}
greet('Alice'); // "Hello, Alice!"
greet(null); // "Hello, Guest!"
greet(); // "Hello, Guest!"
- 객체 속성 읽기
const config = {
port: 0,
host: undefined,
};
const port = config.port ?? 3000; // 0은 nullish 값이 아니므로 그대로 사용
const host = config.host ?? 'localhost'; // undefined이므로 기본값 사용
console.log(port); // 0
console.log(host); // "localhost"
falsy 값이란?
- JavaScript에서 조건문에서 false로 평가되는 값
- 이는 Boolean 컨텍스트(논리적 평가)에서 false처럼 작동
- JavaScript에서 falsy로 간주되는 값들
- false
- 0 (숫자 0)
- -0 (음수 0)
- 0n (BigInt 0)
- "" (빈 문자열)
- null
- undefined
- NaN (Not-a-Number)
- falsy가 아닌 모든 값은 truthy로 간주
if (0) {
console.log("참입니다");
} else {
console.log("거짓입니다");
}
// 출력: "거짓입니다"
if ("") {
console.log("참입니다");
} else {
console.log("거짓입니다");
}
// 출력: "거짓입니다"
console.log(Boolean(0)); // false
console.log(Boolean("")); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined));// false
console.log(Boolean(NaN)); // false
BigInt
- 큰 정수를 처리하는 데이터 타입
const big = 123456789012345678901234567890n;
console.log(big + 1n); // 123456789012345678901234567891n
Dynamic Imports
- 동적으로 모듈을 로드
- 기존 정적 import
- 컴파일 시점에 모듈이 로드
- 항상 상단에 선언
- 모듈을 조건적으로 가져올 수 없음
import { add } from './math.js'; // 정적 import
console.log(add(2, 3));
- 동적 import
- 런타임 시점에 모듈을 로드
- 필요할 때만 모듈을 로드 가능
- 함수 형태로 호출되며, Promise를 반환
// 동적 import
import('./math.js').then((module) => {
console.log(module.add(2, 3)); // 5
});
- Dynamic Import 문법
import(modulePath)
.then((module) => {
// 모듈 사용
})
.catch((error) => {
// 에러 처리
});
- 조건부 로드
- 사용자의 언어나 환경에 따라 특정 모듈만 로드 가능
const loadLanguage = async (language) => {
if (language === 'en') {
const enModule = await import('./lang/en.js');
console.log(enModule.default.greeting); // "Hello"
} else if (language === 'ko') {
const koModule = await import('./lang/ko.js');
console.log(koModule.default.greeting); // "안녕하세요"
}
};
loadLanguage('ko');
- 코드 분할
- 애플리케이션의 특정 기능이 자주 사용되지 않는다면 해당 기능을 동적으로 로드하여 초기 로딩 시간을 감소시킬 수 있음
document.getElementById('loadChart').addEventListener('click', async () => {
const { renderChart } = await import('./chart.js');
renderChart(); // 차트 렌더링 실행
});
- Dynamic Import의 이점
- 코드 스플리팅 : 필요한 시점에만 코드를 로드하여 초기 번들 크기를 줄임
- 조건부 로드 : 특정 조건에 따라 모듈을 로드 가능
- Lazy Loading : 사용자가 요청했을 때만 모듈을 로드하여 성능 최적화
- ES 모듈과 호환 : Promise를 반환하므로 비동기 처리와 연계하기 쉬움
- Dynamic Import의 제한사항
- Dynamic Imports는 Promise 기반이므로 비동기 처리를 고려
- 반드시 경로를 문자열로 제공해야 하며 상대 경로를 지정해야 함
- Webpack 등의 번들러를 사용할 경우 코드 분할 지원이 필요
ECMAScript 2021 (ES12)
String.prototype.replaceAll
- 모든 매칭된 문자열을 교체
'abcabc'.replaceAll('a', 'x'); // 'xbcxbc'
Logical Assignment Operators
- 논리 연산과 할당을 결합한 연산자
- OR 할당 연산자 → ||= : 좌항의 값이 falsy(거짓 같은 값)일 경우에만 우항의 값을 할당
// ES12 이전 방식
let a = 0;
if (!a) {
a = 42;
}
// ES12 이후 방식
let a = 0;
a ||= 42; // a가 falsy 값이므로 42가 할당됨
console.log(a); // 42
let b = "Hello";
b ||= "World"; // b가 truthy 값이므로 기존 값을 유지
console.log(b); // "Hello"
- AND 할당 연산자 → &&= : 좌항의 값이 truthy(참 같은 값)일 경우에만 우항의 값을 할당
// ES12 이전 방식
let a = 1;
if (a) {
a = 42;
}
// ES12 이후 방식
let a = 1;
a &&= 42; // a가 truthy 값이므로 42가 할당됨
console.log(a); // 42
let b = 0;
b &&= "World"; // b가 falsy 값이므로 기존 값을 유지
console.log(b); // 0
- Nullish 할당 연산자 → ??= : 좌항의 값이 null 또는 undefined일 경우에만 우항의 값을 할당
// ES12 이전 방식
let a = null;
if (a === null || a === undefined) {
a = 42;
}
// ES12 이후 방식
let a = null;
a ??= 42; // a가 null이므로 42가 할당됨
console.log(a); // 42
let b = 0;
b ??= "default"; // b가 null 또는 undefined가 아니므로 기존 값을 유지
console.log(b); // 0
Numeric Separators
- 숫자 리터럴에 _를 사용해 가독성 개선
const largeNumber = 1_000_000; // 1000000
Promise.any
- Promise 객체를 처리하는 새로운 메서드로 여러 Promise 중 가장 첫 번째 성공한 Promise를 반환
- 모든 Promise가 실패(reject)할 경우 모든 실패 이유를 포함한 AggregateError 객체를 반환
- 기존 메서드와 비교
- Promise.all
- 모든 Promise가 성공해야 결과를 반환
- 하나라도 실패하면 전체가 실패로 처리됨
- Promise.race
- 성공 여부와 관계없이 가장 빨리 처리된 Promise의 결과를 반환
- Promise.any (ES12)
- 첫 번째로 성공한 Promise의 결과를 반환
- 모든 Promise가 실패하면 AggregateError 반환
- Promise.all
Promise.all([p1, p2, p3])
.then((result) => console.log(result))
.catch((error) => console.error(error)); // 하나라도 실패하면 전체 실패
Promise.race([p1, p2, p3])
.then((result) => console.log(result)) // 가장 빨리 처리된 Promise 반환 (성공, 실패 무관)
.catch((error) => console.error(error));
Promise.any([p1, p2, p3])
.then((result) => console.log(result)) // 가장 먼저 성공한 Promise 반환
.catch((error) => console.error(error)); // 모든 Promise가 실패한 경우만 에러
- 예제1) 첫 번째로 성공한 Promise 반환
- Promise.any는 가장 먼저 성공한 p2의 결과 "Result 2"를 반환
- p1이 실패했더라도 상관하지 않음
const p1 = Promise.reject("Error 1");
const p2 = new Promise((resolve) => setTimeout(() => resolve("Result 2"), 100));
const p3 = new Promise((resolve) => setTimeout(() => resolve("Result 3"), 200));
Promise.any([p1, p2, p3])
.then((result) => console.log(result)) // "Result 2"
.catch((error) => console.error(error));
- 예제2) 모든 Promise가 실패한 경우
- 모든 Promise가 실패했으므로 catch 블록에서 AggregateError가 반환
- AggregateError.errors 속성을 통해 모든 실패 이유를 확인 가능
const p1 = Promise.reject("Error 1");
const p2 = Promise.reject("Error 2");
Promise.any([p1, p2])
.then((result) => console.log(result))
.catch((error) => {
console.error(error.name); // "AggregateError"
console.error(error.message); // "All Promises were rejected"
console.error(error.errors); // ["Error 1", "Error 2"]
});
- 예제3) 빠른 응답을 위한 적용
- Promise.any는 여러 소스에서 데이터를 받아오는 경우 가장 먼저 처리되는 결과를 사용하는 데 유용
- 가장 빠르게 응답한 서버의 결과(fetchFromServer2)를 반환
- 느린 서버 응답이나 실패한 요청은 무시
const fetchFromServer1 = new Promise((resolve) =>
setTimeout(() => resolve("Server 1 Response"), 300)
);
const fetchFromServer2 = new Promise((resolve) =>
setTimeout(() => resolve("Server 2 Response"), 100)
);
const fetchFromServer3 = Promise.reject("Server 3 Failed");
Promise.any([fetchFromServer1, fetchFromServer2, fetchFromServer3])
.then((result) => console.log(result)) // "Server 2 Response"
.catch((error) => console.error(error));
ECMAScript 2022 (ES13)
Class Fields
- 클래스 필드를 선언하는 새로운 문법
- ES13 이전의 클래스는 필드 선언이 없었고 필드를 선언하려면 반드시 생성자 내부에서 this 키워드를 통해 초기화하였으나
필드 선언이 가능하게 되었고 선언된 필드는 기본적으로 public - 필드 앞에 #을 붙이게 되면 private로 선언되고 접근 시도시 SyntaxError 발생
class MyClass {
field = 'value';
#privateField = 'private';
}
Array.prototype.at
- 배열의 마지막 또는 특정 인덱스를 간단히 접근
const arr = [1, 2, 3];
console.log(arr.at(-1)); // 3
728x90
'Language > Javascript' 카테고리의 다른 글
[개념] ES6이란? (1) | 2024.12.24 |
---|---|
[Javascript] DOM 렌더링 시점 (1) | 2024.12.09 |
[Javascript] DOM (Document Object Model)의 개념 (0) | 2024.12.09 |
[Javascript] 비동기 프로그래밍 [Callback, Promise, async/await] (0) | 2024.12.09 |
[Javascript] Javascript 동작 원리 [동기, 비동기] (0) | 2024.12.06 |