HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
🤩
개발
/
NodeJs
NodeJs
/
Javascript
Javascript
/
🪕
ES2015+
🪕

ES2015+

const, letvar 🆚 const, letconst 🆚 let템플릿 문자열( ` . Tab위의 백틱)객체 리터럴메서드 축약 표현프로퍼티 축약 표현계산된 프로퍼티 이름화살표 함수(lambda)구조분해 할당클래스프로미스Promise.resolve, reject, allasync/await
  • 2015년 자바스크립트 문법에 매우 큰 변화가 있었음
  • 2020년 ES2020 까지 나왔고 인터넷 익스플로러와 같은 구형 브라우저에서는 최신 문법 사용할 수 없지만, 요즘에는 babel 처럼 구형 브라우저에 맞게 문법 변환해주는 도구가 널리 쓰이므로 큰 문제 안됨

const, let

if (true) { var x= 3; } console.log(x); // 3 if (true) { const y = 3; } console.log(y); // Uncaught ReferenceError

var 🆚 const, let

  • const, let은 블록 스코프를 가지므로 블록 밖에서는 변수에 접근할 수 없음
  • var는 함수 스코프를 가지므로 if 문의 블록과 관계없이 접근 가능함. var 는 블록 레벨 스코프를 지원하지 않아서 의도치 않게 전역 변수로 선언될 수 있음

const 🆚 let

const a = 0; a = 1; // Uncaught TypeError : Assignment to constant variable let b= 0; b = 1; // 1 const c; // Uncaught SyntaxError: Missing Initializer in const declaration
  • const는 한 번 값을 할당하면 다른 값 할당 x, 초기화 할 때 값 할당하지 않으면 에러 발생
  • let은 할당하고 나서 변경 가능

템플릿 문자열( ` . Tab위의 백틱)

//ES5 문법 var num1 = 1; var num2 = 2; var result = 3; var string1 = num1 + ' 더하기 ' + num2 + '는 \'' + result + '\''; console.log(string1); // 1 더하기 2는 '3' // ES2015 템플릿 문자열 const string = `${num3} 더하기 ${num4} 는 '${result}'`; // 숫자앞에 0 채워넣기 -> String class의 API 이용 const n = 1; n.toString().padStart(2, "0") // 01

객체 리터럴

  • 객체 리터럴
    • { firstName: 'Jane', lastName: 'Doe' }
// 이전 버전 var sayNode = function() { console.log('Node'); } var es = 'ES'; var oldObject = { sayJs: function() { console.log('JS'); } sayNode: sayNode, }; oldObject[es + 6] = 'Fantastic'; oldObject.sayNode(); oldObject.sayJs(); console.log(oldObject.ES6); // 새로 쓰는 버전 const newObject = { sayJs() { console.log('JS'); }, sayNode, [es + 6]: 'Fantastic', };

메서드 축약 표현

  • 위의 newObject에서 보면 함수 정의 시 function 생략하고 바로 정의 가능

프로퍼티 축약 표현

  • 속성명과 변수명이 동일한 경우에는 한 번만 써도 됨 (sayNode : sayNode → sayNode)

계산된 프로퍼티 이름

  • 객체의 속성명을 동적으로 정의할 시, 원래는 객체 리터럴 바깥에서 [es + 6]를 해야 했지만 이제는 객체 리터럴 안에서 바로 정의 가능함

화살표 함수(lambda)

function add1(x, y){ return x + y; } const add2 = (x, y) => { return x + y; }; const add3 = (x, y) => x + y; const add4 = (x, y) => (x + y); function not1(x) { return !x; } const not2 = x => !x;
  • 함수 내부에 return 밖에 없으면 return 문 줄일 수 있음
  • not2 처럼 매개변수가 한 개면 매개변수를 소괄호로 묶지 않아도 됨
var relationship1 = { name: 'zero', friends: ['nero', 'hero', 'xero'], logFriends: function () { var that = this; // relationship1을 가리키는 this를 that에 저장 this.friends.forEach(function (friend) { console.log(that.name, friend); }); }, }; relationship1.logFriends(); const relationship2 = { name: 'zero', friends: ['nero', 'hero', 'xero'], logFriends() { this.friends.forEach(friend => { console.log(this.name, friend); }); }, }; relationship2.logFriends();
기존 function 과 다른 점은 this 바인드 방식임
  • 기존의 function() 으로 정의할 때는 각자 다른 함수 스코프의 this를 가지게 되므로 that이라는 변수를 새로 사용해서 relationship1에 접근함
  • 그러나 화살표 함수를 씀으로 바깥 스코프인 logFriends()의 this를 그대로 사용할 수 있음. ↔ 화살표 함수는 자신만의 this가 없음. 위 스코프의 this를 이어 받음
  • ES6에서는 함수 내부에서 this가 전역객체를 바라보는 문제를 보완하고자, this를 바인딩하지 않는 화살표 함수를 도입. 화살표 함수는 실행 컨텍스트를 생성할 때 this 바인딩 과정 자체가 빠지게 되어, 상위 스코프의 this를 그대로 이용할 수 있음

구조분해 할당

//이전 var candyMachine = { status: { name: 'node', count: 5, }, getCandy: function () { this.status.count--; return this.status.count; }, }; var getCandy = candyMachine.getCandy; var count = candyMachine.status.count; // 변경 const candyMachine = { status: { name: 'node', count: 5, }, getCandy() { this.status.count--; return this.status.count; }, }; const { getCandy, status: { count } } = candyMachine;
객체의 속성을 같은 이름의 변수에 대입하는 코드
const array = ['nodeJs', {}, 10, true]; const [node, obj, , bool] = array;
배열 할당

클래스

클래스 문법도 추가되었지만 다른 언어처럼 클래스 기반으로 동작하는 것이 아니라 여전히 프로토타입 기반으로 동작함
var Human = function(type) { this.type = type || 'human'; }; Human.isHuman = function(human) { return human instanceof Human; } Human.prototype.breathe = function() { alert('h-a-a-a-m'); }; var Zero = function(type, firstName, lastName) { Human.apply(this, arguments); this.firstName = firstName; this.lastName = lastName; }; Zero.prototype = Object.create(Human.prototype); Zero.prototype.constructor = Zero; // 상속하는 부분 Zero.prototype.sayName = function() { alert(this.firstName + ' ' + this.lastName); }; var oldZero = new Zero('human', 'Zero', 'Cho'); Human.isHuman(oldZero); // true
ES2015 이전의 프로토타입 상속 예제 코드
class Human { constructor(type = 'human') { this.type = type; } static isHuman(human) { return human instanceof Human; } breathe() { alert('h-a-a-a-m'); } } class Zero extends Human { constructor(type, firstName, lastName) { super(type); this.firstName = firstName; this.lastName = lastName; } sayName() { super.breathe(); alert(`${this.firstName} ${this.lastName}`); } } const newZero = new Zero('human', 'Zero', 'Cho'); Human.isHuman(newZero); // true
클래스 기반 코드
  • 생성자 함수는 constructor
  • Human.isHuman 같은 클래스 함수 static 키워드로 전환
  • 프로토타입 함수들도 모두 class 블록 안에 포함되어 어떤 함수가 어떤 클래스 소속인지 보기 쉬움
  • 상속도 간단해져서 extends로 쉽게 상속 가능. 다만 이렇게 클래스 문법으로 바뀌었더라도 자바스크립트는 프로토타입 기반으로 동작!

프로미스

  • 이벤트 리스너를 사용할 때 콜백함수를 자주 사용하는데, ES2015부터는 자바스크립트와 노드의 API 들이 콜백 대신 프로미스 기반으로 재구성됨. 악명 높은 콜백 지옥(callback hell) 현상을 극복.
const condition = true; // true면 resolve, false면 reject const promise = new Promise((resolve, reject) => { if (condition) { resolve('성공'); } else { reject('실패'); } }); // 다른 코드가 들어갈 수 있음 promise .then((message) => { console.log(message); // 성공(resolve)한 경우 실행 }) .catch((error) => { console.error(error); // 실패(reject)한 경우 실행 }) .finally(() => { // 끝나고 무조건 실행 console.log('무조건'); });
  • 먼저 프로미스 객체 생성
    • 내부에 resolve와 reject를 매개변수로 갖는 콜백 함수를 넣음
  • promise 변수에 then과 catch 메서드를 붙일 수 있음
    • resolve가 호출되면 then이 실행
      • resolve(’성공’)이 호출되면 then의 message가 ‘성공’이 됨
    • reject가 호출되면 catch가 실행
      • reject(’실패’)가 호출되면 catch의 error가 ‘실패’가 되는 것임
  • finally 부분은 성공/실패 여부와 상관없이 실행됨
 
  • 프로미스를 쉽게 설명하자면, 실행은 바로 하되 결과값은 나중에 받는 객체
    • 결과값은 실행이 완료된 후 then이나 catch 메서드를 통해서 받음
    • new Promise는 바로 실행, 결과값은 then을 붙였을 때 받음
function findAndSaveUser(Users) { Users.findOne({}, (err, user) => { // 첫 번째 콜백 if (err) { return console.error(err); } user.name = 'zero'; user.save((err) => { // 두 번째 콜백 if (err) { return console.error(err); } Users.findOne({ gender: 'm' }, (err, user) => { // 세 번째 콜백 // 생략 }); }); }); }
콜백으로 사용 시, 함수가 세번 중첩되어 코드 깊이가 계속 깊어짐
function findAndSaveUser(Users) { Users.findOne({}) .then((user) => { user.name = 'zero'; return user.save(); }) .then((user) => { return Users.findOne({ gender: 'm' }); }) .then((user) => { // 생략 }) .catch(err => { console.error(err); }); }
위의 콜백을 promise로 바꾼 예제 코드. Users.findOne과 user.save가 내부에서 Promise 객체를 반환해서 가능한 것임

Promise.resolve, reject, all

const promise1 = Promise.resolve('성공1'); const promise2 = Promise.resolve('성공2'); Promise.all([promise1, promise2]) .then((result) => { console.log(result); // ['성공1', '성공2']; }) .catch((error) => { console.error(error); });
resolve는 즉시 resolve하는 프로미스. reject는 즉시 reject. Promise.all은 여러 프로미스를 모두 resolve할 때까지 기다렸다가 then으로 넘어감
  • 이거 python에서 coroutine들 적용했던 것과 비슷하네

async/await

  • 노드 7.6 버전부터 지원되고 ES2017에서 추가됨
  • 위의 findAndSaveUser를 then 방식으로 작성한 코드를 await/async 를 이용하면 더 간단하게 만들 수 있음
async function findAndSaveUser(Users) { try { let user = await Users.findOne({}); user.name = 'zero'; user = await user.save(); user = await Users.findOne({ gender : 'm' }); // 생략 } catch (error) { console.error(error); } }
const findAndSaveUser = async (Users) => { try { let user = await Users.findOne({}); user.name = 'zero'; user = await user.save(); user = await Users.findOne({ gender: 'm' }); // 생략 } catch (error) { console.error(error); } };
위 함수의 화살표 함수 버전
  • await를 붙이면 해당 프로미스가 resolve 될 때까지 기다린 뒤 다음 로직으로 넘어감(비동기 함수 내부적으로)
    • 다른 로직의 진행을 차단하지는 않음. 비동기이기 때문에
    •