2장 알아두어야 할 자바스크립트
2.1 ES2015
자바스크립트는 매년 새로운 버전으로 업데이트 된다. 노드도 주기적으로 버전을 올리며 변경된 자바스크립트 문법을 반영하고 있다.
이 책은 ES2015의 문법을 다룬다.
2.1.1 const, let
C와 다르게 변수 타입을 따로 지정하지 않아도 된다.
기존에는 var타입을 사용했지만 ES2015이후 const와 let 변수를 사용한다
const와 let은 var(함수 스코프)와 다르게 블럭 스코프이므로 블록 밖에서는 변수에 접근할 수 없습니다.
const는 상수선언, 보통 초기화된 변수에 다른 값이 할당되는 경우는 의외로 적으니 기본적으로 const를 사용하고 다른 값을 할당해야 하는 상황이 생겼을 때 let을 사용하자.
2.1.2 템플릿 문자열
ES2015에서 새로 생긴 문자열이다.
기존 방식은
let num1 = 1;
let num2 = 2;
let result = 3;
let string1 = num1 + ' 더하기 ' + num2 + ' 는 \' ' + result + ' \'';
console.log(string1);
위 처럼 가독성이 떨어진다.
새로운 형태는 아래와 같이 훨씬 깔끔해졌다.
let num1 = 1;
let num2 = 2;
let result = 3;
let string1 = `${num1} 더하기 ${num2}는 '${result}'`;
console.log(string1);
2.1.3 객체 리터럴
let sayNode = function(){
console.log('node');
}
let es = 'ES';
let oldObjec =
{
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.sayNode();
newObject'sayJS();
console.log(newObject.ES6);
더이상 (:)와 function을 붙이지 않아도 됩니다.
sayNode: sayNode처럼 속성명과 변수명이 동일할 경우에 한 번만 써도 됩니다.
2.1.4 화살표 함수
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 not(x){
return !x;
}
const not2 = x => !x;
function 선언 대신 => 기호로 선언 가능, 변수에 대입하면 나중에 재사용도 가능.
화살표 함수에 return문만 있으면 return대신 바로 retrun 할 식을 적으면 된다. 보기좋게 소괄호로 묶어준다.
var relationship1 = {
name: 'zero'
friends: ['nero', 'hero', 'xero'],
logFriends: fucntion() {
var that = this;
this.friends.forEach(friend) {
console.log(that.name, freind);
});
},
};
relationships1.logFreinds();
const relationship2 = {
name: 'zero',
friends: ['nero', 'hero', 'xero'],
logFriends() {'
this.friends,firEach(friend => {
console.lgo(this.name, freind);
});
},
};
relationship2.logFriends();
위에 식은 서로다른 함수 스코프의 this를 가지므로 forEach에서 that 이라는 변수를 사용하여 relationship1에 간접적으로 접근하고 있습니다.
아래 식은 화살표 함수를 사용하여 바깥 스코프인 logFreinds()의 this를 그대로 사용할 수 있습니다. 상위 스코프의 this를 그대로 물려받는 것읍니다.
즉, 기본적으로 화살표 함수를 쓰되, this를 사용해야 하는 경우에는 화살표 함수와 함수 선언문(fuction)중에 하나를 고르면 됩니다.
2.1.5 구조분해 할당
구조분해 할당을 사용하면 객체와 배열로부터 속성이나 요소를 쉽게 꺼낼 수 있습니다.
var candyMachine = {
status: {
name: 'node',
count: 5,
},
getCandy: function (){
this.status.count--;
return this.status.count;
},
};
var getCandy = cndyMachine.getCandy;
var count = candymachine.status.count;
const candyMachine = {
status: {
name: 'node',
count: 5,
},
getCandy() {
this.status.count--;
retun this.status.count;
},
};
const { getCandy, status: {count} } = candyMatione;
candyMachine 객체 안에 속성을 찾아서 변수와 매칭합니다.
count처럼 여러 단계 안의 속성도 찾을 수 있습니다.
getCnady와 count변수가 초기화된 것입니다. 다만, 구조분해 할당을 사용하면 함수의 this가 달라질 수 있습니다. 달라진 this를 원래대로 바꿔주려면 bind 함수를 다로 사용해야 합니다.
배열에 대한 구조분해 할당 문법도 존재합니다.
var array = ['node', {}, 10, true];
var node = array[0];
var obj = array[1];
var bool = array[3];
const array = ['node', {}, 10, true];
const [node, obj, ,bool] = array;
2.1.6 클래스
클래스 문법이 추가되었다. 하지만 다른 언어 처럼 클래스 기반으로 동작하는 것이 아니라 여전히 프로토타입 기반으로 동작합니다. 그저 보기 좋게 클래스로 바꾼 것이라고 이해.
var Human = function(type) {
this.type = type || 'human';
};
Human.isHuma = function(human) {
return human instanceof Human;
}
Human.prototype.breathe = function() {
alert('hammm');
};
var Zero = function(type, fistName, lastName) {
Human.apply(this, arguments);
this.firstName = firstName;
this.lastName = lastName;
};
Zero.prototype = Object.create(Human.prototype);
Zero.prototype.constructor = Zero;
Zero.prototype.sayName = fuction() {
alert(this.firstName + ' ' this.lastName);
};
var oldZero = new Zero('human', 'Zero', 'Cho');
Human.isHuman(oldZero);
Human.apply와 Object.create 부분이 상속받는 부분입니다.
class Huamn {
constructor(type = 'human') {
this.type = type;
}
static isHuman(human) {
return human instanceof Human;
}
breathe() {
alert('hammm');
}
}
class Zero extends Human {
constructor(type, firstName, lastName) {
super(type);
this.firstName = firstName;
this.lastName = lastName;
}
sayName(){
super.breathe();
alter~~~;
}
}
const newZero = new Zero('human', 'Zero', 'Cho');
Human.isHuman(newZero);
전반적으로 class안으로 그룹화된 것을 볼 수 있습니다. 생성자 함순느 constructor 안으로 들어갔고, Human.isHuman 같은 클래스 함수는 static 키워드로 전환되었습니다.. 프로토타입 함수들도 모두 class 블록 안에 포함되어 어떤 함수가 어떤 클래스 소속인지 보기 쉽습니다. 상속도 간단해져서 extends 키워드로 쉽게 상속 가능합니다. 다만, 이렇게 class 문법으로 바뀌었더라도 자바스크립트는 프로토타입 기반으로 동작한다는 것을 명심해야 합니다.
2.1.7 프로미스
자바스크립트는 노드에서는 주로 비동기로 접한다.특히 이벤트 리스터를 사용할 때 콜백 함수를 자주 사용합니다. 하지만 ES2015부터는 자바스크럽트와 노드의 api들이 콜백 대신 프로미스를 기반으로 재구성되어 콜백 지옥 현상을 극복했다는 평가를 받고있습니다.
const condition = true;
const promise = new promise((resolve, reject) => {
if(condition) {
resolve('성공');
}else {
reject('실패');
}
});
promise
.then((message) => {
console.log(message);
})
.catch((error) => {
console.error(error);
})
.finally(() => {
console.log('무조건');
});
new promise로 프로미스를 생성할 수 있으며 그 내부에 resolve와 reject를 매개변수로 갖는 콜백함수를 넣는다. 이렇게 생성된 promise변수에 then과 catch 메서드를 붙일 수 있다.
resolve가 호출되면 then, reject가 호출되면 catch가 실행된다. finally는 무조건 실행
resolve와 reject에 넣어준 인수는 각각 then과 catch의 매개변수에서 받을 수 있다.
프로미스를 쉽게 설명하면 실행은 바로하되 결괏값은 나중에 받는 객체이다.
then이나 catch에서 다시 다른 then이나 catch를 붙일수 있다. 이전 return 값을 다시 then의 매개변수로 넘깁니다. 단, then에서 new Promise를 return해야 다음 then에서 받을 수 있다.
프로미스를 여러 개를 한 번에 실행할 수 있는 방법은 Promise.all을 활용하면 간단하다.
const promise1 = Promise.resolve('성공1');
const promise2 = Promise.resolve('성공2');
Promise.all([promise1, promise2])
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error(error);
};
Promise.all은 하나만 실패해도 catch로 간다.
allSettled로 실패한 것만 추려내는것도 가능하다.
2.1.8 async/await
프로미스를 좀더 깔끔하게 줄여주는 문법
function findAndSaveUser(Users) {
Users.findOne({})
.then((user) => {
user.name = 'zero';
return user.save();
})
.then((user) => {
return Users.findOne({ gender: 'm'});
})
.catch(err => {
console.error(err);
});
}
위 코드를 async/await 문법을 사용하면 다음과 같다
async function findAndSaveUser(Users) {
let user = await Users.findOne({});
user.name = 'zero';
user = await user.save();
user = await Users.findOne({ gender: 'm' });
}
이제 함수는 resolve가 될 떄까지 기다린 다음 넘어갑니다.
에러부분 추가작업은 아래와 같습니다.
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);
}
}
for문과 같이 써서 프로미스를 순차적으로 실행할 수 있다.(ES2018+)
const promise1 = Promise.resolve('성공1');
const promise2 = Promise.resolve('성공2');
(async () => {
for await (promise of [promise1, promise2]) {
console.log(promise);
}
})();
async 함수의 반환값은 항상 Promise로 감싸진다. 따라서 실행 후 then 을 붙이거나 또 다른 async 함수 안에서 await을 붙여서 처리할 수 있다.
async function findAndSaveUser(Users) {
}
findAndSaveUser().then(() => {..});
async function other() {
const result - await findAndSaveUSer();
}