본문 바로가기
읽은 책

JavaScript Patterns

by 쑤구니 2010. 11. 2.
Javascript Patterns (Paperback) - 10점
Stefanov, Stoyan/Oreilly & Associates Inc

2012.11.5 팀 세미나로 다시 시작

2장 기초

  1. 유지보수 가능한 코드 작성 : 시간이 지나면 코드를 개발한 사람이건 다른 사람이건 버그를 고치는데 많은 시간이 소요된다. 언제 다시 보더라도 코드를 읽는데/이해하는데 걸리는 시간이 최소화 되도록 코드를 작성해야 한다.
  2. 전역 변수 최소화 : 변수의 범위는 함수이고, 함수에 속하지 않는 변수는 전역 변수이다.
    1. 전역 변수의 문제점 : 이름 충돌이 발생할 수 있다. 의도치 않게 전역변수가 되지 않도록 하기 위해 var로 항상 선언해라. var a = b = 0;에서 b는 선언되지 않으므로 전역 변수가 될 수 있다.
    2. var 선언을 빼먹었을 때의 부작용 : var로 선언하지 않은 변수는 전역 객체의 프로퍼티가 되므로 delete로 삭제할 수 있지만, 그렇지 않으면 삭제할 수 없다.
      전역 scope에서 var로 선언해도 전역 객체의 프로퍼티가 되는거 아니었나??? 확인!!
    3. 전역 객체에 대한 접근 : 환경에 따라 window가 전역 객체가 아닐 수 있다. 그러므로 window를 직접 사용하지 말고 책에 있는데로 전역 객체를 변수에 할당해 사용해라. 참고로 ES5에서는 책과는 다른 방법 사용. 확인!!!
    4. 단일 var 패턴 : 함수 상단에 var 선언을 한번만 해라. 장단점이 있긴 한데, 가급적 이 패턴을 사용하는게 좋을듯
    5. 호이스팅: 분산된 var 선언의 문제점 : var 선언 위치에 상관없이 항상 선언된 것으로 봄. 단일 var 패턴을 사용하면 실수하지 않는다.
  3. for 루프 : 배열의 length를 조건으로 할 경우 항상 캐쉬해라. 특히 HTMLCollection은 critical.(HPJS에서 다룬 내용) JSLint에서는 ++ 보다는 += 또는 i + 1를 선호(--도 마찬가지)하는데... 개인적으로는 ++를 쓰는것도 무방한거 같다. 카운트가 증가하는 for문은 감수하는 for 또는 while로 변경할 수 있는데, 변수를 줄일 수 있고 속도가 미세하게 빨라진다(HPJS, 속도는 별 의미 없다)
  4. for-in 루프 : hasOwnProperty로 필터링 해라. 대상 객체가 hasOwnProperty를 덮어쓴 경우 Object.prototype.hasOwnProperty.call(obj, prop)를 사용.
  5. 내장 생성자 프로토타입 확장하기 / 확장하지 않기 : 확장하지 마라. 다음의 조건을 모두 만족하는 경우 사용해도 된다. 1. ECMAScript에 명시되어 있는데, 브라우저에서 구현하지 않은 경우, 2. 지원 브라우저 중 일부에서 제공하고 있는 경우, 3. 문서화한 경우.
  6. switch 패턴 : switch 문의 가독성 높이기 위해 책에 있는 코딩 규칙을 지켜라.
  7. 암묵적 타입캐스팅 피하기 : ===, !==를 반드시 사용해라.
    1. eval() 피하기 : 안티 패턴 - 동적 프로퍼티 접근(eval("obj." + property)), setInterval, setTimeout에서의 eval. 꼭 사용해야 한다면 new Function() 사용을 고려해라. new Function() 안에서 실행되는 코드 내 변수의 유효범위는 함수가 된다.
  8. parseInt()를 통한 숫자 변환 : 두번째 인자인 기수를 반드시 입력해라. parseInt 대신, +를 통한 자동 형변환 또는 Number()를 사용할 수 있다.
  9. 코딩 규칙

3장 리터럴과 생성자

  1. 배열 리터럴
    • 배열 생성자의 특이성 : new Array(3)에서의 인자 3은 배열의 초기 길이이다.
    • 배열인지 판별하는 방법 : typeof [1, 2]; // => "object" 이다. ES5에서는 Array.isArray() 메소드 제공.
      if (typeof Array.isArray === 'undefined') {
        Array.isArray = function(arg) {
          return Object.prototype.toString.call(arg) === '[object Array]';
        };
      }
  2. JSON : 프로퍼티명 항상 따옴표로 감싸야 한다. 함수, 정규식 리터럴이 값이 될 수 없다.
  3. 정규 표현식 리터럴 : 생성자 쓰면 정규식을 string으로 써야 하므로 피곤하다. 이스케이프 때문. 그냥 리터럴로 사용해라. 패턴을 미리 알수 없는 경우는 어쩔수 없이 생성자 사용. 리터럴 사용시 루프안에서 사용하면 한번만 객체 생성-좋은점.
  4. 원시 데이터 타입 래퍼 : 객체형으로 사용할 일 거의 없다.
  5. 에러 객체 : 에러 객체의 프로퍼티는 호스트마다 다를 수 있다. 신뢰할 수 없음. throw 할 때 꼭 Error 객체일 필요 없음.
  6. 요약 : new Date() 제외하고는 생성자 쓸 일 거의 없다. 그냥 리터럴 써라.

5장 객체 생성 패턴

  1. 네임스페이스 패턴 : 객체 리터럴 방식의 네임스페이스 만들기. var MYAPP = {}; MYAPP.Box=function(){} 이런식. 단점은 번거롭다. 결국은 모든 요소들이 public하게 노출된다.
    • 범용 네임스페이스 패턴 : 파일이 분리되면 네임스페이스가 존재하는 경우를 항상 체크하는 코드(var MYAPP = MYAPP || {})가 들어가야 하는데, 네임스페이스의 깊이가 길어지면 복잡해 진다. 함수를 만들어서 사용하자. 문제점 : 이 함수는 어떤 네임스페이스에 포함되는가? 이 함수가 포함되는 네임스페이스는 무조건 독립적으로 만들어야 한다.
  2. 의존 관계 선언 : 긴 네임스페이스를 지역 변수에 참조를 담아서 사용해라. 의존 관계를 의도적으로 드러낸다. 네임스페이스의 프로퍼티에 접근할 때 빠르다. 여러번 반복될 경우 압축(minifier) 효과 있다.
  3. 비공개 프로퍼티와 메소드 : 객체 리터럴, 생성자 함수는 객체의 프로퍼티를 public하게 노출한다.
    • 비공개 멤버 : 생성자에서 클로저를 만들면 private 만들 수 있다. 단 메소드가 prototype에 포함된게 아니라 메모리 사용량 증가한다.
    • 특권 메소드 : 생성자에서 this의 프로퍼티로 만든 메소드. 클로저를 통해 private에 접근 가능하다고 붙인 이름.
    • 비공개 멤버의 허점 : firefox 이슈???, 특권 메소드에서 값이 아닌 참조를 return하면, 결국은 접근 가능해 진다. 이것의 해결책은 필요한 내용만 담은 객체를 새로 만들어서 전달. 최소 권한의 원칙(priciple of least authority, POLA)
    • 객체 리터럴과 비공개 멤버 : 즉시 실행 함수에서 클로저를 만들고 객체 리터럴을 반환하는 방법. 모듈 패턴의 기초
    • 프로토타입과 비공개 멤버 : 생성자에서 this의 프로퍼티를 만들면, 모든 객체에서 동일한 프로퍼티 존재함. 메소드는 중복 문제 있음. prototype 만들 때 객체 리터럴을 이용해 비공개 멤버 만드는 방식 사용할 수 있다.
    • 비공개 함수를 공개 메소드로 노출시키는 방법 : 모듈 노출 패턴, 객체 리터럴과 비공개 멤버와 뭐가 다른거지?
  4. 모듈 패턴 : 만드는 순서는 1. 모듈 선언(네임스페이스 생성), 2. 모듈 정의, 즉시 실행 함수를 이용해 비공개 유효범위를 가지는 객체 반환.
    • 모듈 노출 패턴 : 모듈 패턴에서 공개할 함수를 객체 리터럴에서가 아닌 즉시 실행 함수 범위내에서 생성하면 된다.
    • 생성자를 생성하는 모듈 : 모듈 패턴에서 객체가 아닌 생성자 함수를 반환한다.
    • 모듈에 전역 변수 가져오기 : 즉시 실행 함수에 전역 객체를 인자로 전달. 호스트 환경에 따라 사용 가능. Node에서 유행.
  5. 샌드박스 패턴 : 네임스페이스 패턴은 하나의 전역 변수에 의존. 두 버전을 동시에 사용할 수 없다. 프로퍼티 참조시 이름이 길다.
    • 전역 생성자 : 샌드박스를 생성하는 유일한 전역 생성자를 가진다. 생성자에는 콜백 함수가 전달되는데 이 콜백함수가 app이 실행되는 socpe가 되며 전역으로 노출되는 것은 없다. 책에서는 sandbox 생성자의 여러가지 인터페이스를 보여준다.
    • 모듈 추가하기 : Sandbox에 modules라는 프러퍼티를 만들고 이 프로퍼티의 속성으로 필요한 모듈을 정의한다. 각 모듈 정의는 함수이고, 함수가 실행될 때 넘어온 scope에 필요한 프로퍼티들을 붙이게 된다. 책 참조.
    • 생성자 구현 : 여기가 핵심. 인자 처리해서 필요한 모듈을 초기화(실행)하고 최종적으로는 callback 실행
    일단 그림으로는 requirejs의 define와 동일한 방식이다.
  6. 스태틱 멤버 : 인스턴스가 아닌 클래스의 멤버.
    • 공개 스태틱 멤버 : 생성자 함수에 프로퍼티 추가하면 스태틱 멤버 처럼 사용 가능. 단순히 함수에 프로퍼티만 넣으면 인스턴스에서는 사용할 수 없으므로, 함수의 prototype에도 참조 추가한다. 단 이러면 각 사용시 this가 서로 틀려진다. 처음것은 생성자 함수이고, 두번째는 new로 만들어진 객체가 된다. 어어어 여기 좀... 확인 필요., strict mode에서도.
    • 비공개 스태틱 멤버 : 생성자 함수 만들때 즉시 실행 함수 내에서 다른 함수를 반환하는 방법 사용하고, 즉시 실행 함수 내에서 정의된 변수, 함수는 비공개 static 멤버가 된다.
  7. 객체 상수 : 상수는 보통 명명규칙으로 해결. 모두 대문자. 객체의 상수 프로퍼티는 생성자 함수의 프로퍼티로 정의. 책에 상수 정의하는 함수 있는데... 명명 규칙으로 해결하는게 방법일듯.
  8. 체이닝 패턴 : 메소드를 호출한 결과가 자기 객체를 반환하게 해서 메소드 호출을 계속할 수 있게 함.
    • 체이닝 패턴의 장단점 : 코드가 짧아지고 읽기 좋다. 단일 작업을 하는 작은 함수를 만들게 유도한다. 디버깅 하기 힘들다. jQuery에서 사용하는 패턴
  9. method() 메소드 : prototype의 프로퍼티로 함수를 추가하는 syntatic sugar. 구현은 책 참조

6장 코드 재사용 패턴

  1. 클래스 방식 vs 새로운 방식의 상속 패턴 : Java 방식의 상속을 클래스 방식이라고 하고, 그 외의 것들을 모두 새로운 방식이라고 하자. 자바 방식은 Person p = new Person();. 유사하게 JavaScript에서는 var p = new Person();로 가능.
  2. 클래스 방식의 상속을 사용할 경우 예상되는 산출물 : function Parent(name) { }; function Child(name) { }; inherit(Child, Parent);에서 inherit 함수가 상속 구조를 만든다.
  3. 클래스 방식의 상속 패턴 #1 - 기본 패턴 : Parent의 인스턴스를 Chld의 prototype으로 설정한다.
  4. 클래스 방식의 상속 패턴 #1 - 기본 패턴 :
  5. 클래스 방식의 상속 패턴 #1 - 기본 패턴 :
  6. 클래스 방식의 상속 패턴 #1 - 기본 패턴 :
  7. 클래스 방식의 상속 패턴 #1 - 기본 패턴 :
  8. 클래스 방식의 상속 패턴 #1 - 기본 패턴 :