
화살표 함수 · 스프레드 문법 · 구조 분해 할당
ES6에서 도입된 화살표 함수는 function 키워드 대신 => 를 써서 함수를 더 간단하게 표현하는 문법이다. 항상 익명 함수로 정의되고, 본문이 짧은 함수에서 특히 유용하다.
let message;
// 기존 함수 표현식
message = function () {
return 'Hello World!';
};
console.log(message());
// 화살표 함수로 변경
message = () => {
return 'Arrow Function!';
};
console.log(message());
// 한 줄 본문 + 표현식이면 중괄호와 return 생략
message = () => 'Arrow Functions are Simple!';
console.log(message());
// 매개변수 여러 개
message = (val1, val2) => 'Arrow ' + val1 + val2;
console.log(message('Function', '!'));
// 매개변수 하나면 괄호 생략 가능
message = val => 'Arrow ' + val;
console.log(message('Functions are GOOD!!!'));
객체를 바로 반환할 때는 소괄호로 감싸야 한다. 그렇지 않으면 {} 가 함수 몸체로 해석된다.
const createUser = (id, name) => ({ id, name });
console.log(createUser(1, '유관순'));
const createUser2 = (id, name) => { id, name };
console.log(createUser2(2, '홍길동')); // undefined
console.log([1, 2, 3, 4, 5].map(function (val) { return val * 10; }));
console.log([1, 2, 3, 4, 5].map(val => val * 10));
기존 콜백보다 훨씬 간결해진다.
화살표 함수는 자신의 this 를 갖지 않고, 바깥 스코프의 this를 그대로 캡처한다.
let theater = {
store: '건대점',
titles: ['어벤져스', '겨울왕국', '스파이더맨', '라이온킹', '알라딘'],
showMovieList() {
// 화살표 함수: 바깥 this = theater
this.titles.forEach(title => console.log(this.store + ' : ' + title));
// 일반 함수: this는 전역 객체(window/global)를 가리킨다
this.titles.forEach(function (title) {
console.log(this); // 전역
console.log(this.store + ' : ' + title); // undefined
});
}
};
theater.showMovieList();
콜백 내부에서 바깥 객체의 this 를 그대로 쓰고 싶을 때 화살표 함수가 매우 편하다.
화살표 함수는 this 와 prototype 을 갖지 않기 때문에 생성자 함수로 쓸 수 없다.
const arrowFunc = () => {};
// new arrowFunc(); // TypeError: arrowFunc is not a constructor
console.log(arrowFunc.hasOwnProperty('prototype')); // false
화살표 함수도 super 를 직접 가지지 않고, 상위 스코프의 super 를 그대로 가져다 쓴다.
class Animal {
constructor(name, weight) {
this.name = name;
this.weight = weight;
}
move(lostWeight) {
if (this.weight > lostWeight) this.weight -= lostWeight;
console.log(`${this.name}(은)는 움직임으로 인해 ${lostWeight}kg만큼 감량되어 ${this.weight}kg가 되었다.`);
}
}
class Tiger extends Animal {
move(lostWeight) {
// 화살표 함수: 바깥의 super 를 그대로 사용
setTimeout(() => super.move(lostWeight), 3000);
// 일반 함수로 super 사용하면 SyntaxError
// setTimeout(function(){ super.move(lostWeight) }, 3000);
console.log('먹이를 찾아 산기슭을 어슬렁~');
}
}
let tiger = new Tiger('백두산 호랭이', 90);
tiger.move(1);
화살표 함수는 자체 arguments 객체도 갖지 않고 상위 스코프의 arguments 를 참조한다.
(function () {
const arrowFunc = () => console.log(arguments);
arrowFunc(3, 4); // [1, 2] 출력 (즉시 실행 함수의 arguments)
})(1, 2);
그래서 화살표 함수에서 가변 인수를 다루고 싶으면 arguments 대신 나머지 매개변수(...args) 를 쓰는 쪽이 자연스럽다.
함수에 정의된 매개변수보다 적게 인수를 넘기면 undefined, 많이 넘기면 초과 인수는 버려진다.
나머지 매개변수는 남은 인수들을 배열로 한 번에 받는 문법이다. 항상 매개변수 목록의 마지막에 둔다.
function merge(msg1, msg2) {
return msg1 + msg2;
}
console.log(merge('안녕하세요.'));
console.log(merge('안녕하세요.', '반갑다.'));
console.log(merge('안녕하세요.', '반갑다.', '제 이름은 홍길동이다.')); // 초과 인수 무시
function mergeAll(...args) {
let message = '';
for (let arg of args) message += arg;
return message;
}
console.log(mergeAll('안녕하세요.'));
console.log(mergeAll('안녕하세요.', '반갑다.'));
console.log(mergeAll('안녕하세요.', '반갑다.', '제 이름은 홍길동이다.'));
나머지 매개변수가 인수 목록 → 배열 로 모으는 역할이라면,
스프레드 문법은 그 반대로 배열 → 인수 목록 으로 펼치는 역할을 한다.
console.log(`가장 큰 값 : ${Math.max(10, 20, 30)}`);
let arr = [10, 20, 30];
// 배열 그대로 넘기면 제대로 동작하지 않는다
console.log(`가장 큰 값 : ${Math.max(arr)}`);
// 스프레드 문법으로 배열을 펼친다
console.log(`가장 큰 값 : ${Math.max(...arr)}`);
여러 배열을 섞거나, 일반 값과 함께 사용할 수도 있다.
let arr1 = [10, 30, 20];
let arr2 = [100, 300, 200];
console.log(`가장 작은 값 : ${Math.min(...arr1, ...arr2)}`);
console.log(`가장 작은 값 : ${Math.min(1, ...arr1, 2, ...arr2, 3)}`);
let arr = [10, 20, 30];
let arr2 = [100, 200, 300];
// concat 보다 간결
let merged = [0, ...arr, 2, ...arr2];
console.log(merged);
let str = 'JavaScript';
console.log([...str]); // ['J','a','v','a','S','c','r','i','p','t']
// 배열 복사
let arr = [10, 20, 30];
let arrCopy = [...arr];
console.log(arr);
console.log(arrCopy);
console.log(arr === arrCopy); // false
// 객체 복사
let obj = { name: '홍길동', age: 20 };
let objCopy = { ...obj };
console.log(obj);
console.log(objCopy);
console.log(obj === objCopy); // false
스프레드 복사는 얕은 복사(shallow copy) 라는 점을 기억해 두면 좋다.
스프레드 문법(spread)
나머지 매개변수(rest)
둘을 함께 쓰면 인수 목록과 배열 사이를 매우 쉽게 오갈 수 있다.
배열이나 객체에서 값을 꺼낼 때 obj.a, arr[0] 처럼 매번 접근하지 않고,
패턴을 기준으로 한 번에 변수에 할당하는 문법이다.
let nameArr = ['Gildong', 'Hong'];
// 기존 방식
// let firstName = nameArr[0];
// let lastName = nameArr[1];
let [firstName, lastName] = nameArr;
console.log(firstName); // Gildong
console.log(lastName); // Hong
반환 값이 배열인 함수에도 그대로 쓸 수 있다.
let [firstName2, lastName2] = 'Saimdang Shin'.split(' ');
console.log(firstName2); // Saimdang
console.log(lastName2); // Shin
let arr = ['firstName', 'middleName', 'lastName'];
let [firstName3, , lastName3] = arr;
console.log(firstName3); // firstName
console.log(lastName3); // lastName
let student = '학생';
let teacher = '선생님';
[student, teacher] = [teacher, student];
console.log(`student : ${student}, teacher : ${teacher}`);
let [sign1, sign2, ...rest] = ['양자리', '황소자리', '쌍둥이자리', '게자리', '사자자리'];
console.log(sign1); // 양자리
console.log(sign2); // 황소자리
console.log(rest); // ['쌍둥이자리', '게자리', '사자자리']
let [firstName4 = '아무개', lastName4 = '김'] = ['길동'];
console.log(firstName4); // '길동' (배열 값)
console.log(lastName4); // '김' (기본 값)
let user = {};
[user.firstName, user.lastName] = 'Gwansoon Yu'.split(' ');
console.log(user); // { firstName: 'Gwansoon', lastName: 'Yu' }
let pants = {
productName: '배기팬츠',
color: '검정색',
price: 30000
};
let { productName, color, price } = pants;
console.log(productName);
console.log(color);
console.log(price);
변수 이름을 바꾸고 싶으면 프로퍼티: 변수명 형태로 쓴다.
let { color: co, price: pr, productName: pn } = pants;
console.log(co);
console.log(pr);
console.log(pn);
let { productName: onlyName } = pants;
console.log(`productName : ${onlyName}`);
객체에 존재하지 않는 프로퍼티에는 기본값을 줄 수 있다.
let shirts = {
productName: '베이직 셔츠'
};
let {
productName: pn,
color: co = '어떤 색상',
price: pr = 0
} = shirts;
console.log(pn); // '베이직 셔츠'
console.log(co); // '어떤 색상'
console.log(pr); // 0
let { productName: productName2, ...rest } = pants;
console.log(productName2); // '배기팬츠'
console.log(rest); // { color: '검정색', price: 30000 }
객체 리터럴과 헷갈리지 않도록 소괄호로 감싼다.
let productName3, color3, price3;
// { productName: productName3, color: color3, price: price3 } = pants; // SyntaxError
({ productName: productName3, color: color3, price: price3 } = pants);
console.log(productName3);
console.log(color3);
console.log(price3);
객체 안에 객체/배열이 중첩된 경우에도 한 번에 꺼낼 수 있다.
let product = {
size: {
width: 10,
height: 30
},
items: ['doll', 'robot']
};
let {
size: { width, height },
items: [item1, item2],
producer = '홍길동'
} = product;
console.log(width); // 10
console.log(height); // 30
console.log(item1); // 'doll'
console.log(item2); // 'robot'
console.log(producer); // '홍길동' (기본값)
매개변수가 많을 때, 또는 기본값이 필요할 때 구조 분해를 쓰면 코드가 훨씬 읽기 좋아진다.
function displayProduct(producer = '아무개', width = 0, height = 0, items = []) {}
displayProduct('신사임당', undefined, undefined, ['Coffee', 'Donut']);
// 기본값을 쓰려고 undefined 를 중간중간 넘겨야 해서 지저분해진다.
function displayProduct2({
producer = '아무개',
width = 0,
height = 0,
items = []
}) {
console.log(producer);
console.log(width);
console.log(height);
console.log(items);
}
let exampleProduct = {
items: ['Coffee', 'Donut'],
producer: '신사임당'
};
displayProduct2(exampleProduct);
정리하면,
화살표 함수
function 보다 문법이 간결하고, this / super / arguments 를 가지지 않고 바깥 스코프를 캡처한다.나머지 매개변수 / 스프레드 문법
(...args)), 매개변수 마지막에서만 사용...arr)구조 분해 할당
이 세 가지 문법은 ES6 이후 자바스크립트 코드 스타일의 핵심 축이라, 한 번 익혀 두면 이후 코드 작성과 리팩터링이 훨씬 수월해진다.