🪕
ES2015+
- 2015년 자바스크립트 문법에 매우 큰 변화가 있었음
- 2020년 ES2020 까지 나왔고 인터넷 익스플로러와 같은 구형 브라우저에서는 최신 문법 사용할 수 없지만, 요즘에는 babel 처럼 구형 브라우저에 맞게 문법 변환해주는 도구가 널리 쓰이므로 큰 문제 안됨
const, let
var 🆚 const, let
- const, let은 블록 스코프를 가지므로 블록 밖에서는 변수에 접근할 수 없음
- var는 함수 스코프를 가지므로 if 문의 블록과 관계없이 접근 가능함. var 는 블록 레벨 스코프를 지원하지 않아서 의도치 않게 전역 변수로 선언될 수 있음
const 🆚 let
- const는 한 번 값을 할당하면 다른 값 할당 x, 초기화 할 때 값 할당하지 않으면 에러 발생
템플릿 문자열( ` . Tab위의 백틱)
객체 리터럴
메서드 축약 표현
- 위의 newObject에서 보면 함수 정의 시 function 생략하고 바로 정의 가능
프로퍼티 축약 표현
- 속성명과 변수명이 동일한 경우에는 한 번만 써도 됨 (sayNode : sayNode → sayNode)
계산된 프로퍼티 이름
- 객체의 속성명을 동적으로 정의할 시, 원래는 객체 리터럴 바깥에서 [es + 6]를 해야 했지만 이제는 객체 리터럴 안에서 바로 정의 가능함
화살표 함수(lambda)
- 함수 내부에 return 밖에 없으면 return 문 줄일 수 있음
- not2 처럼 매개변수가 한 개면 매개변수를 소괄호로 묶지 않아도 됨
- 기존의 function() 으로 정의할 때는 각자 다른 함수 스코프의 this를 가지게 되므로 that이라는 변수를 새로 사용해서 relationship1에 접근함
- 그러나 화살표 함수를 씀으로 바깥 스코프인 logFriends()의 this를 그대로 사용할 수 있음. ↔ 화살표 함수는 자신만의 this가 없음. 위 스코프의 this를 이어 받음
- ES6에서는 함수 내부에서
this
가 전역객체를 바라보는 문제를 보완하고자, this
를 바인딩하지 않는 화살표 함수를 도입. 화살표 함수는 실행 컨텍스트를 생성할 때 this
바인딩 과정 자체가 빠지게 되어, 상위 스코프의 this
를 그대로 이용할 수 있음
구조분해 할당
클래스
클래스 문법도 추가되었지만 다른 언어처럼 클래스 기반으로 동작하는 것이 아니라 여전히 프로토타입 기반으로 동작함
- Human.isHuman 같은 클래스 함수 static 키워드로 전환
- 프로토타입 함수들도 모두 class 블록 안에 포함되어 어떤 함수가 어떤 클래스 소속인지 보기 쉬움
- 상속도 간단해져서 extends로 쉽게 상속 가능. 다만 이렇게 클래스 문법으로 바뀌었더라도 자바스크립트는 프로토타입 기반으로 동작!
프로미스
- 이벤트 리스너를 사용할 때 콜백함수를 자주 사용하는데, ES2015부터는 자바스크립트와 노드의 API 들이 콜백 대신 프로미스 기반으로 재구성됨. 악명 높은 콜백 지옥(callback hell) 현상을 극복.
- 먼저 프로미스 객체 생성
- 내부에 resolve와 reject를 매개변수로 갖는 콜백 함수를 넣음
- promise 변수에 then과 catch 메서드를 붙일 수 있음
- resolve가 호출되면 then이 실행
- resolve(’성공’)이 호출되면 then의 message가 ‘성공’이 됨
- reject가 호출되면 catch가 실행
- reject(’실패’)가 호출되면 catch의 error가 ‘실패’가 되는 것임
- finally 부분은 성공/실패 여부와 상관없이 실행됨
- 프로미스를 쉽게 설명하자면, 실행은 바로 하되 결과값은 나중에 받는 객체
- 결과값은 실행이 완료된 후 then이나 catch 메서드를 통해서 받음
- new Promise는 바로 실행, 결과값은 then을 붙였을 때 받음
Promise.resolve, reject, all
- 이거 python에서 coroutine들 적용했던 것과 비슷하네
async/await
- 노드 7.6 버전부터 지원되고 ES2017에서 추가됨
- 위의 findAndSaveUser를 then 방식으로 작성한 코드를 await/async 를 이용하면 더 간단하게 만들 수 있음
- await를 붙이면 해당 프로미스가 resolve 될 때까지 기다린 뒤 다음 로직으로 넘어감(비동기 함수 내부적으로)
- 다른 로직의 진행을 차단하지는 않음. 비동기이기 때문에
if (true) {
var x= 3;
}
console.log(x); // 3
if (true) {
const y = 3;
}
console.log(y); // Uncaught ReferenceError
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
//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',
};
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;
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 바인드 방식임//이전
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
클래스 기반 코드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('무조건');
});
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 객체를 반환해서 가능한 것임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으로 넘어감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);
}
};
위 함수의 화살표 함수 버전