본 내용은 클린코드 자바스크립트 강의내용 요약에 대한 내용과
airbnb javascript style guide 의 내용 중 제가 알던 부분과 상충되는 부분을 위주로 정리한 내용을 다룹니다.
강의 요약의 경우, 기존 작성된 markdown파일이 무너지는 경우가 발생하여 단순히 notion의 링크를 추가하였습니다.
https://github.com/airbnb/javascript 를 기반으로 한다.
작성된 모든 style-guide 에 대한 내용을 다루지 않는다.
유효하지 않은 식별자명이 섞여있을 경우, 해당부분만 따옴표처리.
// bad
const bad = {
'foo': 3,
'bar': 4,
'data-blah': 5,
};
// good
const good = {
foo: 3,
bar: 4,
'data-blah': 5,
};
hasOwnProperty같은 Object.prototype methods를 직접 호출하지 않는다.
해당 객체 내 동일한 이름의 속성에 의해 가려질 수 있기 때문.
// bad
console.log(object.hasOwnProperty(key));
// good
console.log(Object.prototype.hasOwnProperty.call(object, key));
// best
const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope.
console.log(has.call(object, key));
유사배열 객체를 배열로 전환하기 위해서는 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.from()과 Array.prototype.map() 모두 기존 배열 각 요소에 함수를 적용해 새 배열을 만든다.
Array.from()는 유사배열 객체, 이터러블 객체를 배열로 바꿔주는 기능을 가지고 있다.
그 외 일반 배열에 대해서는 똑같이 사용할 수 있다.
// bad
const baz = [...foo].map(bar);
// good
const baz = Array.from(foo, bar);
반복 동작되는 경우에 10 이상으로 조금만 커지게 되어도 속도적 측면에서 Array.prototype.map()이 압도적으로 유리하다.
단순히 lint 측면에서 Array.from()이 우선시 되는 것인지 의문이다.
non-function block(if, while etc)에서는 함수를 선언하지 않도록 한다.
루프 내에 함수를 작성하면 함수가 루프문 주위로 클로저를 생성하는 방식으로 작동해 오류가 생긴다.
함수를 변수에 할당하는 것으로 문제를 완화할 수는 있다.
// bad
if (currentUser) {
function test() {
console.log('Nope.');
}
}
// good
let test;
if (currentUser) {
test = () => {
console.log('Yup.');
};
}
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// good
function concatenateAll(...args) {
return args.join('');
}
매개변수, 특히 arguments에 대해서 재할당이 발생하지 않도록 한다.
// bad
function f1(obj) {
obj.key = 1;
}
// good
function f2(obj) {
const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
}
prototype을 직접 manipulating할 바에 class형 프로그래밍
// bad
function Queue(contents = []) {
this.queue = [...contents];
}
Queue.prototype.pop = function () {
const value = this.queue[0];
this.queue.splice(0, 1);
return value;
};
// good
class Queue {
constructor(contents = []) {
this.queue = [...contents];
}
pop() {
const value = this.queue[0];
this.queue.splice(0, 1);
return value;
}
}
single module을 export할 때는 defalut export가 선호됨.
// bad
export function foo() {}
// good
export default function foo() {}
파일 이름 확장자 비포함이 선호됨. (몰랐음)
// bad
import foo from './foo.js';
import baz from './baz/index.jsx';
// good
import foo from './foo';
import baz from './baz';
iterators를 사용하지 말 것.
대신 javascript의 고차함수를 이용한다.
const numbers = [1, 2, 3, 4, 5];
// bad
const increasedByOne = [];
for (let i = 0; i < numbers.length; i++) {
increasedByOne.push(numbers[i] + 1);
}
// good
const increasedByOne = [];
numbers.forEach((num) => {
increasedByOne.push(num + 1);
});
// best (keeping it functional)
const increasedByOne = numbers.map((num) => num + 1);
변수 할당을 합리적인 위치에 해야함 까지 지적;
// bad - unnecessary function call
function checkName(hasName) {
const name = getName();
if (hasName === 'test') {
return false;
}
if (name === 'test') {
this.setName('');
return false;
}
return name;
}
// good
function checkName(hasName) {
if (hasName === 'test') {
return false;
}
const name = getName();
if (name === 'test') {
this.setName('');
return false;
}
return name;
}
3항 연산자를 기피하는 것이 더 나을 수도 있음.
// bad
const foo = a ? a : b;
const bar = c ? true : false;
const baz = c ? false : true;
// good
const foo = a || b;
const bar = !!c;
const baz = !c;