github에 올라와있는 https://github.com/airbnb/javascript 를 번역하고 약간의 해석을 덧붙인 글입니다.
배열을 만들 때, 리터럴 사용하기.
// bad
const items = new Array();
// good
const items = [];
리터럴 구문: 대괄호([]
)를 사용해 새 객체를 작성하는 것을 배열 리터럴 생성이라고 한다.
조금 더 직관적이게 []
를 사용하는 것을 추천하고 있다.
배열에서 직접적인 할당을 사용하는 대신, push를 사용해 아이템들을 넣기.
const someStack = [];
// bad
someStack[someStack.length] = 'abracadabra';
// good
someStack.push('abracadabra');
여러 정보를 찾아보니, 가독성을 위해 push를 사용하는 것 같았다. 큰 성능 차이는 없다고 한다.(어차피 배열에 새로운 값을 추가한다는 건 둘 다 똑같기 때문)
배열을 복사할 때, ...(스프레드 연산자)를 사용해라.
// bad
const len = items.length;
const itemsCopy = [];
let i;
for (i = 0; i < len; i += 1) {
itemsCopy[i] = items[i];
}
// good
const itemsCopy = [...items];
ES6에서 생겨난 스프레드 연산자(...)는 여러모로 유용하다.
배열을 복사하는 방법은 2가지가 있는데,
얕은 복사(배열 참조): 복사한 배열이 기존 배열을 참조해 값을 변경하면 다른 배열 값 또한 변경된다.
깊은 복사: 기존 배열과 다른 또다른 배열을 생성(단 내용은 같음)
bad 케이스
말고도, slice, map을 사용해 깊은 복사를 할 수 있었지만, ES6에서 생긴 스프레드 연산자는 이를 훨씬 쉽게 만들어준다.
순회 가능한 객체를 배열로 만들 때, Array.from 대신에 스프레드 연산자(...)를 사용해라.
const foo = document.querySelectorAll('.foo');
// good
const nodes = Array.from(foo);
// best
const nodes = [...foo];
iterable objects
: objects such as Map and Set
여기서 말하는 '순회 가능한 객체'의 예시는 아래 코드와 같다.
// case 1
function f(){
return [...arguments];
}
// case 2
function f(){
return Array.from(arguments);
}
만약 위의 코드를f(1,2,3)
로 실행시켜 준다면, 스프레드 연산자를 통해
순회 가능한 객체
=> 배열
로 변할 것이다.
Array.from은 얕게 복사해 새로운 Array를 만드는 일을 하는데, 이는 스프레드와 크게 다르지 않아 더욱 직관적인 스프레드 연산자(...)를 사용하라고 권장하고 있다.
유사 배열 객체를 배열로 변환하기 위해서 Array.from을 사용해라.
const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
// bad
const arr = Array.prototype.slice.call(arrLike);
// good
const arr = Array.from(arrLike);
array-like object
: objects with a length property and indexed elements
유사 배열 객체는 길이 프로퍼티, index된 요소들이 존재하는 객체를 말하는데,
직접 프로퍼티에 접근하는 방법보다 단순히 Array.from을 통해 요소를 가져오는 방법이 더 효과적이다.
순회를 위해 map을 사용할 때, 스프레드 연산자(...) 대신에 Array.from을 사용해라.
// bad
const baz = [...foo].map(bar);
// good
const baz = Array.from(foo, bar);
위의 코드에서의 bar와 foo는 다음과 같다고 가정하자.
const bar = function(input){console.log(input)}
const foo = [1,2,3,4,5]
bar는 함수의 파라미터로 받은 값을 콘솔에 출력해주는 함수이고,
foo는 1부터 5까지의 요소가 들어있는 배열이다.
bad 방법을 사용한다면,
[...foo]
를 통해 배열을 복사하고, map으로 복사한 배열을 순회시키는 과정을 거친다.
good 방법을 사용하면,
Array.from에 들어온 foo배열은 bar에 따라 새로운 값이 만들어지거나 결과가 return 된다.
결국, 스프레드 연산자(...)는 해당 과정에서 배열을 불필요하게 복사하는 과정을 거치기 때문에, 지양해야 한다.
배열 메서드 콜백들에 대해 return 문을 사용해라.
간단하게 말해서, 메열 메서드를 사용하는 경우, return이 항상 존재하게 하라는 뜻이다.
// good (case 1)
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
// good (case 2)
[1, 2, 3].map((x) => x + 1);
// bad - no returned value means `acc` becomes undefined after the first iteration (case 3)
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
const flatten = acc.concat(item);
});
// good (case 4)
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
const flatten = acc.concat(item);
return flatten;
});
// bad (case 5)
inbox.filter((msg) => {
const { subject, author } = msg;
if (subject === 'Mockingbird') {
return author === 'Harper Lee';
} else {
return false;
}
});
// good (case 6)
inbox.filter((msg) => {
const { subject, author } = msg;
if (subject === 'Mockingbird') {
return author === 'Harper Lee';
}
return false;
});
위의 코드에서 case 3을 살펴보겠다.
return 문을 사용하지 않았기 때문에, 다음에 acc 값이 업데이트 되지 않아 코드가 정상적으로 작동하지 않게 된다.
case 5는 얼핏 보면 괜찮아 보이지만, 사소한 문제가 있다.
함수는 return을 만나면 해당 값을 반환해주며 함수를 종료해주는데,
이러한 특징 때문에 if-else만 존재하는 경우, 굳이 else를 만들지 않고도 if문 바깥에서 return을 해주어도 같은 결과가 나온다.
배열이 여러 줄인 경우(중첩 배열), 처음과 끝 대괄호(괄호)에 줄바꿈을 해준다.
// bad
const arr = [
[0, 1], [2, 3], [4, 5],
];
const objectInArray = [{
id: 1,
}, {
id: 2,
}];
const numberInArray = [
1, 2,
];
// good
const arr = [[0, 1], [2, 3], [4, 5]];
const objectInArray = [
{
id: 1,
},
{
id: 2,
},
];
const numberInArray = [
1,
2,
];
이건 가독성과 관련된 팁인데,
배열이 너무 길거나, 중첩된 배열일 경우, 처음( [ )과 끝( ] ) 대괄호에 줄바꿈을 해주는 것이 좋다.
하지만, 이는 가독성을 위한 것으로 꼭 필수되어야 하는 것은 아닌, 단순한 권장 사항이다.
사람마다 기준이 다르기 때문에, preiiter과 같은 익스텐션을 사용해 공통된 코딩 format 규칙을 지키면서 코드를 짜는 것이 좋다.