-
자동차 경주 게임 미션 공통 피드백(1)et cetera/TIL 2021. 4. 12. 01:59반응형
■ 인덱스를 통해 찾는 방법 지양하기
const targetButton = document.getElementByTagName("button")[0];
위 코드의 경우, 첫 번째 button 태그를 선택하도록 구현이 되어있는데,
'첫 번째'라는 조건에 종속되어있다는 게 문제.
document에는 언제, 어디에서든지 button element가 추가될 수 있는데,
이 targetButton이라는 건 전체 문서에서 꼭 첫 번째에 존재해야하기 때문에
버그가 발생할 가능성이 높아진다.
즉, 이 버튼만의 고유한 값으로 접근을 하는 것이 좋다는 뜻.
■ 빠른 실패
// not recommended if (value >= 4) { this.#position++; } // recommended if (value < 4) return; this.#position++;
아래의 형태를 사용하면 좋은 점
1. depth가 줄어든다(if문으로 감싸는 게 많아지면 가독성이 안좋아진다).
2. bad case를 먼저 정의하여, 코드의 예외를 메소드의 상단에서 바로 직관적으로 알 수 있다.
3. >= 의 연산 코스트보다 < 와 같은 단일 비교가 더 성능이 좋다.
(인터프리터 언어가 어셈블리 및 로우 레벨의 코드로 변환되어
하드웨어의 레지스터에서 연산이 이루어질 때 >=와 <는 줄 수가 다르다고 함.
한 줄 한 줄이 쌓여서 레지스터 연산 줄 수가 많아지면 성능 차이가 생각보다 크다고 한다)
■ 한 함수는 한 가지 기능만
const isValidRacingCount = (racingCount) => { if (racingCount < VALIDATOR.MIN_COUNT) { alert(ERR_MESSAGE.COUNT_TOO_SMALL); return false; } return true; };
함수의 이름은 값을 검증하는 기능만 하는 것처럼 보이는데(is~),
실제로 함수의 내부를 살펴보면 해당 함수가 alert를 하는 역할까지 맡고 있다는 걸 알 수 있다.
함수의 이름만 보고도 함수가 무슨 일을 하고 있는지 명확하게 알 수 있어야 하고,
함수의 이름에 맞게 딱 한 가지의 일만 수행하도록 작성하는 것이 좋다.
여기서는 alert 함수를 외부로 추출하는 것이 좋겠다.
■ Chaining methods의 활용
메소드 체이닝을 활용하면 코드를 훨씬 간결하게 만들 수 있다.
// 우승자 이름을 배열로 리턴한다. // not recommended export const getWinner = () => { state.cars.sort((a, b) => { return b.totalStep - a.totalStep; }); const maxTotalStep = state.cars[0].totalStep; const winners = state.cars.filter((car) => { if (car.totalStep === maxTotalStep) { return car; } }); const winnerNames = winners.map((winner) => { return winner.name; }); return winnerNames; }; // recommended const maxTotalStep = Math.max(...cars.map((car) => car.totalStep), 0) const winnerNames = cars .filter((car) => car.totalStep === maxTotalStep) .map((car) => car.name)
■ 구조분해할당의 활용
// not recommended const button1 = document.getElementsByTagName("button")[0]; const button2 = document.getElementsByTagName("button")[1]; const button3 = document.getElementsByTagName("button")[2]; // recommended const [button1, button2, button3] = document.getElementsByTagName("button");
■ class 문법 뿐만아니라, prototype에 대한 이해도 함께 가져가야 한다.
■ 일반 for문의 사용을 자제할 것
for문은 반복 인덱스를 카운팅하는 변수와 조건, 그리고 인덱스를 하나씩 올리는 것까지
전부 제어를 해줘야 하기 때문에 인간이 실수할 수 있는 여지가 너무 많다.
실무에서도 for문은 잘 사용하지 않는다고 함.
Array의 메서드들을 적극적으로 사용하면 코드가 한 층 깔끔해지고, 유지보수도 더 쉬워진다.
(forEach, map, filter, reduce, every, some 등)
■ Optional Chaining
// 1 const nestedProp = obj.first && obj.first.second; // Optional Chaining of 1 const nestedProp = obj.first?.second; // 2 if (!this.props.cars || !this.props.cars.value) return; // Optional Chaining of 2 this.props?.cars?.value?
1을 보면, obj.first의 값은 obj.first.second의 값에 접근하기 전에 null(or undefined)가 아니라는 것을 검증한다.
이는 만약의 경우, obj.first를 테스트 없이 obj.first.second에 직접 접근할 때 일어날 수 있는 에러를 방지한다.
그러나 optional chaining을 사용하면 명시적으로 테스트를 하지 않아도 된다.
. 대신에 ?. 를 사용함으로써, 자바스크립트는 object.first.second 에 접근하기 전에
obj.first가 null(or undefined)가 아니라는 것을 암묵적으로 확인한다.
만약 obj.first가 null or undefined라면, nestedProp은 자동으로 단락되어 undefined를 반환한다.
1의 optional chaning을 풀어쓰면 다음과 같다.
const nestedProp = ((obj.first === null || obj.first === undefined) ? undefined : obj.first.second);
※ Optional Chaining 더 자세히
1. 존재하지 않을 수도 있는 메서드를 호출할 때에도 optional chaining을 사용할 수 있다.
함수 호출과 optional chaining을 사용하면, 메서드를 찾을 수 없는 경우에 예외를 발생시키는 것 대신에
해당 표현식이 자동으로 undefined를 반환하게 할 수 있다.
(만약 속성에 해당 이름이 있지만 함수가 아니라면, ?. 의 사용은 여전히 예외를 발생시킨다.
TypeError exception: customMethod is not a function)
const result = someInterface.customMethod?.();
2. Optional chaining으로 객체의 속성에 접근할 때, 대괄호 표기법도 사용 가능
const nestedProp = obj?.['prop' + 'Name'];
3. Optional chaining으로 배열 항목에도 접근 가능
const arrayItem = arr?.[42];
※ Nullish coalescing operator(Null 병합 연산자)
널 병합 연산자(??)는 왼쪽 피연산자가 null or undefined일 때 오른쪽 피연산자를 반환하고,
그렇지 않으면 왼쪽 피연산자를 반환하는 논리 연산자이다.
왼쪽 피연산자가 null or undefined 뿐만 아니라, 모든 falsy값(0, '', false, NaN, ...)일 때
오른쪽 피연산자를 반환하는 OR 논리 연산자와는 다르다.
즉, 0이나 빈 문자열('')을 유효한 값으로 사용하고자 할 때 null 병합 연산자를 사용하면 된다.
const foo = null ?? 'default string'; console.log(foo); // default string const bar = 0 ?? 42; console.log(bar); // 0
* AND나 OR 논리 연산자와 직접적으로 결합해서 사용하면 SyntaxError가 발생한다.
괄호를 사용하여 우선순위를 명시적으로 나타내주면 에러 없이 사용 가능.
null || undefined ?? 'foo'; // SyntaxError true || undefined ?? 'foo'; // SyntaxError (null || undefined) ?? 'foo'; // 'foo'
■ return 값을 활용하고 있는지 고려하기
// not recommended if (!isValidRacingCount(racingCount)) { return ($racingCountInput.value = ''); } // recommended if (!isValidRacingCount(racingCount)) { $racingCountInput.value = ''; return }
함수 내부에 이런 return문이 있다고 치자.
실제 이 $racingCountInput.value 값을 함수 외부에서 받아서 사용할 의도가 아니라면,
그 의도가 명확히 드러나도록 아래쪽의 코드와 같이 분리해서 작성하는 게 좋음.
코드를 항상 짧게 작성하는 것만이 좋은 건 아니다.
코드에 의도를 명확히 드러내는 것이 더 중요함.
■ no-prototype-builtins
ESLint의 no-prototype-builtins라는 규칙이 있는데,
이는 Object.prototype의 builtin으로 제공되는 메서드를 객체에서 직접 호출하지 않도록 하는 규칙.
이 규칙을 통해 객체에서 builtin 메서드를 직접 호출하지 않도록 하는 이유는 다음과 같다.
1. Object.create(null)
Object.create에 null을 인자로 주어 객체를 생성하면 Object.prototype을 상속받지 않는다.
그래서 Object.create(null)로 생성한 객체에서 builtin 메서드를 직접 호출하면 에러를 발생시킨다.
2. 객체의 속성이 builtin method를 override한 경우
객체에 builtin으로 제공되는 메서드와 같은 이름을 가진 속성을 객체가 가지고 있다면
예상치 못한 동작이 발생할 수 있다.
반응형'et cetera > TIL' 카테고리의 다른 글
Javascript Visualized: Prototypal Inheritance (0) 2021.11.27 Javascript Visualized: Javascript Engine (0) 2021.11.27 Javascript Visualized: Event Loop, Hoisting, Scope Chain (0) 2021.11.27 자동차 경주 게임 미션 공통 피드백(2) (0) 2021.04.16 계산기 미션 공통 피드백 (2) 2021.04.10