2021-05-13

JavaScript 안티 패턴

JavaScript 안티 패턴

참조 문서

목차

  1. <script><body>최하단에 배치할 것 +
  2. 외부소스를 로컬로 관리할 것
  3. 전역변수 사용을 하지말 것 +
  4. import는 최상단에 배치할 것 +
  5. 선언없이 변수사용하지 말 것
  6. 생성자(Array/Object/Function)를 사용하지 말것 배열/객체 함수
  7. == 비교연산자를 쓰지 말 것
  8. 중괄호({…})를 생략하지 말것
  9. parseInt()에 진법을 붙일것
  10. switch에서 연속되고 기능이 있는 case사이에 break를 생략하지 말것
  11. 배열 순회시, for-in를 사용하지 말 것
  12. 배열 내에서 delete로 항목을 제거하지 말 것
  13. 반복문에서 절차에 따라 필요한 처리만 진행할 것
  14. 반복문에서 continue처리를 피할 것(불확실)
  15. 반복문안에서 try-catch 를 처리하지 말 것
  16. 돔 객체는 한번만 조회 할 것
  17. 돔 조작을 최소화 할 것
  18. 돔 레이아웃 불필요하게 초기화 하지 말 것
  19. 인라인 메소드 안에서 이벤트 처리를 하지 말 것
  20. eval()를 사용하지 말 것
  21. with()를 사용하지 말 것
  22. setTimeout/setInterval 사용시 문자열로된 callback 사용하지 말 것
  23. 기본 객체를 수정하지 말 것
  24. 증감연산자(++/--)를 사용하지 말 것
  25. this 를 따로 할당해서 쓰지 말 것
  26. 코드끝에 ;을 생략하지 말 것
  27. (ES5)정의하기전에 사용하지 말 것
  28. (Legacy)배열 순회시, array.length값을 정의해서 쓸 것

상세

<script><body>최하단에 배치할 것

script 에 정의된 소스들을 다운받는 과정에서 렌더링이 지연됨 참조

script async
스크립트 다운로드가 완료된 경우, 스크립트 파싱 후 HTML 파싱 재개

script defer
스크립트 다운로드가 완료되더라도 HTML 파싱후, 스크립트 파싱

1. 외부소스를 로컬로 관리할 것

CDN 기능 오류 나 네트워크 문제등의 외부 문제로 오류가 발생하는 것을 방지

2. 전역변수 사용을 하지말 것

다른 위치에서 접근하거나 기능이 덮어써질 가능성이 있음

Namespacing

// Bad - 중복될 가능성이 있음
const name = 'Nicholas'; // window.name === 'Nicholas'
function sayName() {
  alert(name);
}

// Good - Defined a global object MyApp and defined name variable and sayName function inside the MyApp
const MyApp = {
  name: 'Nicholas',
  sayName() {
    alert(this.name);
  }
};

Immediately Invoked Function Expression

// Bad - today variable is global, so still remains after the code has been reset
const today = new Date();

alert(`Today is ${today.getDate()}`);

// Good - today variable is local to an anonymous function, so cannot be accessed from outside of the IIFE
(function() {
  const today = new Date();

  alert( `Today is ${today.getDate()}`);
})();

4. import는 최상단에 배치할 것

가독성 문제

5. 선언없이 변수사용하지 말 것

전역변수로 생성됨으로 추가적인 문제 발생
function 의 경우, eval()을 통해야 함으로 성능 저하 발생

6. 생성자(Array/Object/Function)를 사용하지 말것 배열/객체 함수

성능면이나 가독성 면에서 더 불리함

7. == 비교연산자를 쓰지 말 것

암시적 형변환을 거치게 됨으로 타입오류나 가독성 문제가 발생===

8. 중괄호({…})를 생략하지 말것

가독성 문제

9. parseInt()에 진법을 붙일것

문자열이 0x또는 0X로 시작하면 16 진수를 사용하고 0으로 시작하면 8 진수 또는 10 진수를 사용하여 문제가 발생할 수 있음

// Bad
const month = parseInt('08');
const day = parseInt('09');

// Good
const month = parseInt('08', 10);
const day = parseInt('09', 10);

// Tip : If converting to decimals, using Number() function or '+' operator is faster.
const month = Number('08')
const day = +'09';

10. switch에서 연속되고 기능이 있는 case사이에 break를 생략하지 말것

코드 누락인지 아니면 의도된 절차인지 가독성의 문제 발생

// Bad - case 사이에 다른 처리를 한상태에서 break 없이 다른 case로 넘어가지 않도록
switch (foo) {
  case 1:
    A()
  case 2:
    B();
    break;
  ...
}

11. 배열 순회시, for-in를 사용하지 말 것

for-in 배열 사용시, 배열내의 빈값이 있을때 생략하는 문제가 발생하며. 배열순회 성능면에서도 for-in을 지양하는 것이 좋음. 그러나 가독성 면에서 array.forEach()가 유리한점도 있음.

12. 배열 내에서 delete로 항목을 제거하지 말 것

배열내에 delete로 삭제된 항목이 undefined 로 남아 배열의 전체 길이는 동일하게 유지됨, 빈 배열 항목을 처리한다는 관점에서는 역으로 이용이 가능한 부분?

// Bad
const numbers = ['zero', 'one', 'two', 'three', 'four', 'five'];
delete numbers[2]; // ['zero', 'one', undefined, 'three', 'four', 'five'];

// Good
const numbers = ['zero', 'one', 'two', 'three', 'four', 'five'];
numbers.splice(2, 1); // ['zero', 'one', 'three', 'four', 'five'];

13. 반복문에서 절차에 따라 필요한 처리만 진행할 것

중복되는 변수나 조건에 부합하지 않는 처리를 반복문에서 제외하여 성능 저하를 막을것

14. 반복문에서 continue처리를 피할 것(불확실)

과도하게 사용될경우, 가독성의 문제와 함께 javascript 엔진이 별도의 컨텍스트로 관리하기에 성능문제가 발생할 수 있음

추가로 찾아봤을때, breakcontinue가 나쁜 프로그래밍 관행입니까?에서 처럼 무조건 안쓰는것이 옳다고는 할수 없는 것으로 보인다. What is the point of the no-continue rule?의 내용에서는 countinuego to 처럼 continue label로 처리되는것에 대해 금지한다는 의미인것으로 애기하고 있다.
차라리 filter()의 형태로 정제된 리스트만 처리하면 되지 않을까?

let loopCount = 0;
// Bad
for (let i = 1; i < 10; i += 1) {
  if (i > 5) {
    continue;
  }
  loopCount += 1;
}

// Good
for (let i = 1; i < 10; i += 1) {
  if (i <= 5) {
    loopCount += 1;
  }
}

15. 반복문안에서 try-catch 를 처리하지 말 것

루프가 반복될때마다 예외 개체가 현재 범위에서 저장되기 때문

// Bad
const {length} = array;
for (let i = 0; i < length; i += 1) {
  try {
    ...
  } catch (error) {
    ...
  }
}

// Good
const {length} = array;
function doSomething() {
  try {
    ...
  } catch (error) {
    ...
  }
}
for (let i = 0; i < length; i += 1) {
  doSomething();
}

16. 돔 객체는 한번만 조회 할 것

성능저하 문제

17. 돔 조작을 최소화 할 것

innerHTML, appendChild() 호출시마다 DOM을 다시 그리게되어 성능문제 발생

18. 돔 레이아웃 불필요하게 초기화 하지 말 것

layout thrashing으로 인해 성능저하 문제

layout thrashing
layout thrashing을 강제하는 것
DOM 요소가 변경될 경우, 기존 layout은 무효화 되어 추가로 layout 호출시 새로 계산이 필요하여 성능지연이 발생

// Bad
elementA.className = "a-style";
var heightA = elementA.offsetHeight; // layout is needed
elementB.className = "b-style"; // invalidates the layout
var heightB = elementB.offsetHeight; // layout is needed again

// Good
elementA.className = "a-style";
elementB.className = "b-style";
var heightA = elementA.offsetHeight; // layout is needed and calculated
var heightB = elementB.offsetHeight; // layout is up-to-date (no work)
// Bad
function resizeWidth(paragraphs, boxElement) {
  const {length} = paragraphs;

  for (let i = 0; i < length; i += 1) {
    paragraphs[i].style.width = `${boxElement.offsetWidth}px`;
  }
}

// Good
function resizeWidth(paragraphs, box) {
  const {length} = paragraphs;
  const width = boxElement.offsetWidth;

  for (let i = 0; i < length; i += 1) {
    paragraphs[i].style.width = `${width}px`;
  }
}

19. 인라인 메소드 안에서 이벤트 처리를 하지 말 것

소스 관리나 이벤트 처리 관점에서 혼란을 일으킴

20. eval()를 사용하지 말 것

구문 분석을 처리하는 과정에서 성능 문제가 크게 발생하며 보안측면에서도 매우 문제가 많음

21. with()를 사용하지 말 것

처리되는 과정에 대해 가독성 문제가 있으며 별도의 영역을 생성하여 성능문제도 발생

// Bad
with(document.getElementById('myDiv').style) {
  background = 'yellow';
  color = 'red';
  border = '1px solid black';
}

// Good
const {style} = document.getElementById('myDiv');
style.background = 'yellow';
style.color = 'red';
style.border = '1px solid black';

22. setTimeout/setInterval 사용시 문자열로된 callback 사용하지 말 것

내부적으로 문자열 함수에 대해 eval()이 처리 됨으로 20. 과 같은 문제 발생

// Bad
function callback() {
  ...
}
setTimeout('callback()', 1000);

23. 기본 객체를 수정하지 말 것

예측할 수 없는 오류가 발생할 가능성이 큼, 하지만 polyfill 처럼 긍정적인 사용방향도 있음

Monkey Patch
몽키패치- 런타임 중에 코드를 수정하는 것
Polyfill 처럼 구형 브라우저를 지원하기 위해 구형 브라우저의 코드를 수정해야 하거나, 개발 후 테스트를 진행할때 임시로 데이터 요청을 다른곳으로 요청하던지 하는 방식으로 사용될 수 있다. 그러나 동적으로 수정한다는 점에서 문제의 소지가 될 수 있다는점

24. 증감연산자(++/–)를 사용하지 말 것

i++++i와 같이 처리와 할당의 관점이 혼란을 야기해서 편의에 비해 문제소지가 있음

// Increment
let a = 1;
console.log(a++);    // 1 
console.log(++a);    // 3
// Decrement
let b = 1;
console.log(b--);    // 1
console.log(--b);    // -1

25. this 를 따로 할당해서 쓰지 말 것

클로저로 사용할 수 있지만 가독성의 문제, 관련하여 다른 의견에서는 오히려 명시적인 구분이 된다는점에서 장점도 있다는 의견이 있음

// Bad
function() {
  const self = this;
  return function() {
    console.log(self);
  };
}
function() {
  const that = this;
  return function() {
    console.log(that);
  };
}
function() {
  const _this = this;
  return function() {
    console.log(_this);
  };
}

// Good
function printContext() {
  return function() {
    console.log(this);
  }.bind(this);
}
function printContext() {
  return () => console.log(this);
}

26. 코드끝에 ;을 생략하지 말 것

구문의 분리가 명확하지 않고 내부적으로 누락된 ;을 처리하는 과정에서 성능 문제 발생

27. (ES5)정의하기전에 사용하지 말 것

가독성 문제와 함께 호이스팅으로 인해 혼란 발생

28. (Legacy)배열 순회시, array.length값을 정의해서 쓸 것

IE10 과 같은 브라우저에서는 큰 성능문제를 일으킴