Javascript Patterns (Paperback) - ![]() Stefanov, Stoyan/Oreilly & Associates Inc |
2012.11.5 팀 세미나로 다시 시작
2장 기초
- 유지보수 가능한 코드 작성 : 시간이 지나면 코드를 개발한 사람이건 다른 사람이건 버그를 고치는데 많은 시간이 소요된다. 언제 다시 보더라도 코드를 읽는데/이해하는데 걸리는 시간이 최소화 되도록 코드를 작성해야 한다.
- 전역 변수 최소화 : 변수의 범위는 함수이고, 함수에 속하지 않는 변수는 전역 변수이다.
- 전역 변수의 문제점 : 이름 충돌이 발생할 수 있다. 의도치 않게 전역변수가 되지 않도록 하기 위해
var로 항상 선언해라.var a = b = 0;에서b는 선언되지 않으므로 전역 변수가 될 수 있다. - var 선언을 빼먹었을 때의 부작용 :
var로 선언하지 않은 변수는 전역 객체의 프로퍼티가 되므로delete로 삭제할 수 있지만, 그렇지 않으면 삭제할 수 없다.
전역 scope에서 var로 선언해도 전역 객체의 프로퍼티가 되는거 아니었나??? 확인!! - 전역 객체에 대한 접근 : 환경에 따라
window가 전역 객체가 아닐 수 있다. 그러므로window를 직접 사용하지 말고 책에 있는데로 전역 객체를 변수에 할당해 사용해라. 참고로 ES5에서는 책과는 다른 방법 사용. 확인!!! - 단일 var 패턴 : 함수 상단에
var선언을 한번만 해라. 장단점이 있긴 한데, 가급적 이 패턴을 사용하는게 좋을듯 - 호이스팅: 분산된 var 선언의 문제점 : var 선언 위치에 상관없이 항상 선언된 것으로 봄. 단일 var 패턴을 사용하면 실수하지 않는다.
- 전역 변수의 문제점 : 이름 충돌이 발생할 수 있다. 의도치 않게 전역변수가 되지 않도록 하기 위해
- for 루프 : 배열의 length를 조건으로 할 경우 항상 캐쉬해라. 특히 HTMLCollection은 critical.(HPJS에서 다룬 내용) JSLint에서는
++보다는+=또는i + 1를 선호(--도 마찬가지)하는데... 개인적으로는++를 쓰는것도 무방한거 같다. 카운트가 증가하는 for문은 감수하는 for 또는 while로 변경할 수 있는데, 변수를 줄일 수 있고 속도가 미세하게 빨라진다(HPJS, 속도는 별 의미 없다) - for-in 루프 :
hasOwnProperty로 필터링 해라. 대상 객체가hasOwnProperty를 덮어쓴 경우Object.prototype.hasOwnProperty.call(obj, prop)를 사용. - 내장 생성자 프로토타입 확장하기 / 확장하지 않기 : 확장하지 마라. 다음의 조건을 모두 만족하는 경우 사용해도 된다. 1. ECMAScript에 명시되어 있는데, 브라우저에서 구현하지 않은 경우, 2. 지원 브라우저 중 일부에서 제공하고 있는 경우, 3. 문서화한 경우.
- switch 패턴 : switch 문의 가독성 높이기 위해 책에 있는 코딩 규칙을 지켜라.
- 암묵적 타입캐스팅 피하기 :
===,!==를 반드시 사용해라.- eval() 피하기 : 안티 패턴 - 동적 프로퍼티 접근(
eval("obj." + property)),setInterval,setTimeout에서의 eval. 꼭 사용해야 한다면new Function()사용을 고려해라.new Function()안에서 실행되는 코드 내 변수의 유효범위는 함수가 된다.
- eval() 피하기 : 안티 패턴 - 동적 프로퍼티 접근(
- parseInt()를 통한 숫자 변환 : 두번째 인자인 기수를 반드시 입력해라.
parseInt대신,+를 통한 자동 형변환 또는Number()를 사용할 수 있다. - 코딩 규칙
3장 리터럴과 생성자
- 배열 리터럴
- 배열 생성자의 특이성 :
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]'; }; }
- 배열 생성자의 특이성 :
- JSON : 프로퍼티명 항상 따옴표로 감싸야 한다. 함수, 정규식 리터럴이 값이 될 수 없다.
- 정규 표현식 리터럴 : 생성자 쓰면 정규식을 string으로 써야 하므로 피곤하다. 이스케이프 때문. 그냥 리터럴로 사용해라. 패턴을 미리 알수 없는 경우는 어쩔수 없이 생성자 사용. 리터럴 사용시 루프안에서 사용하면 한번만 객체 생성-좋은점.
- 원시 데이터 타입 래퍼 : 객체형으로 사용할 일 거의 없다.
- 에러 객체 : 에러 객체의 프로퍼티는 호스트마다 다를 수 있다. 신뢰할 수 없음.
throw할 때 꼭Error객체일 필요 없음. - 요약 :
new Date()제외하고는 생성자 쓸 일 거의 없다. 그냥 리터럴 써라.
5장 객체 생성 패턴
- 네임스페이스 패턴 : 객체 리터럴 방식의 네임스페이스 만들기.
var MYAPP = {}; MYAPP.Box=function(){}이런식. 단점은 번거롭다. 결국은 모든 요소들이 public하게 노출된다.- 범용 네임스페이스 패턴 : 파일이 분리되면 네임스페이스가 존재하는 경우를 항상 체크하는 코드(
var MYAPP = MYAPP || {})가 들어가야 하는데, 네임스페이스의 깊이가 길어지면 복잡해 진다. 함수를 만들어서 사용하자. 문제점 : 이 함수는 어떤 네임스페이스에 포함되는가? 이 함수가 포함되는 네임스페이스는 무조건 독립적으로 만들어야 한다.
- 범용 네임스페이스 패턴 : 파일이 분리되면 네임스페이스가 존재하는 경우를 항상 체크하는 코드(
- 의존 관계 선언 : 긴 네임스페이스를 지역 변수에 참조를 담아서 사용해라. 의존 관계를 의도적으로 드러낸다. 네임스페이스의 프로퍼티에 접근할 때 빠르다. 여러번 반복될 경우 압축(minifier) 효과 있다.
- 비공개 프로퍼티와 메소드 : 객체 리터럴, 생성자 함수는 객체의 프로퍼티를 public하게 노출한다.
- 비공개 멤버 : 생성자에서 클로저를 만들면 private 만들 수 있다. 단 메소드가 prototype에 포함된게 아니라 메모리 사용량 증가한다.
- 특권 메소드 : 생성자에서
this의 프로퍼티로 만든 메소드. 클로저를 통해 private에 접근 가능하다고 붙인 이름. - 비공개 멤버의 허점 : firefox 이슈???, 특권 메소드에서 값이 아닌 참조를 return하면, 결국은 접근 가능해 진다. 이것의 해결책은 필요한 내용만 담은 객체를 새로 만들어서 전달. 최소 권한의 원칙(priciple of least authority, POLA)
- 객체 리터럴과 비공개 멤버 : 즉시 실행 함수에서 클로저를 만들고 객체 리터럴을 반환하는 방법. 모듈 패턴의 기초
- 프로토타입과 비공개 멤버 : 생성자에서
this의 프로퍼티를 만들면, 모든 객체에서 동일한 프로퍼티 존재함. 메소드는 중복 문제 있음.prototype만들 때 객체 리터럴을 이용해 비공개 멤버 만드는 방식 사용할 수 있다. - 비공개 함수를 공개 메소드로 노출시키는 방법 : 모듈 노출 패턴, 객체 리터럴과 비공개 멤버와 뭐가 다른거지?
- 모듈 패턴 : 만드는 순서는 1. 모듈 선언(네임스페이스 생성), 2. 모듈 정의, 즉시 실행 함수를 이용해 비공개 유효범위를 가지는 객체 반환.
- 모듈 노출 패턴 : 모듈 패턴에서 공개할 함수를 객체 리터럴에서가 아닌 즉시 실행 함수 범위내에서 생성하면 된다.
- 생성자를 생성하는 모듈 : 모듈 패턴에서 객체가 아닌 생성자 함수를 반환한다.
- 모듈에 전역 변수 가져오기 : 즉시 실행 함수에 전역 객체를 인자로 전달. 호스트 환경에 따라 사용 가능. Node에서 유행.
- 샌드박스 패턴 : 네임스페이스 패턴은 하나의 전역 변수에 의존. 두 버전을 동시에 사용할 수 없다. 프로퍼티 참조시 이름이 길다.
- 전역 생성자 : 샌드박스를 생성하는 유일한 전역 생성자를 가진다. 생성자에는 콜백 함수가 전달되는데 이 콜백함수가 app이 실행되는 socpe가 되며 전역으로 노출되는 것은 없다. 책에서는 sandbox 생성자의 여러가지 인터페이스를 보여준다.
- 모듈 추가하기 : Sandbox에 modules라는 프러퍼티를 만들고 이 프로퍼티의 속성으로 필요한 모듈을 정의한다. 각 모듈 정의는 함수이고, 함수가 실행될 때 넘어온 scope에 필요한 프로퍼티들을 붙이게 된다. 책 참조.
- 생성자 구현 : 여기가 핵심. 인자 처리해서 필요한 모듈을 초기화(실행)하고 최종적으로는 callback 실행
define와 동일한 방식이다. - 스태틱 멤버 : 인스턴스가 아닌 클래스의 멤버.
- 공개 스태틱 멤버 : 생성자 함수에 프로퍼티 추가하면 스태틱 멤버 처럼 사용 가능. 단순히 함수에 프로퍼티만 넣으면 인스턴스에서는 사용할 수 없으므로, 함수의 prototype에도 참조 추가한다. 단 이러면 각 사용시
this가 서로 틀려진다. 처음것은 생성자 함수이고, 두번째는new로 만들어진 객체가 된다. 어어어 여기 좀... 확인 필요., strict mode에서도. - 비공개 스태틱 멤버 : 생성자 함수 만들때 즉시 실행 함수 내에서 다른 함수를 반환하는 방법 사용하고, 즉시 실행 함수 내에서 정의된 변수, 함수는 비공개 static 멤버가 된다.
- 공개 스태틱 멤버 : 생성자 함수에 프로퍼티 추가하면 스태틱 멤버 처럼 사용 가능. 단순히 함수에 프로퍼티만 넣으면 인스턴스에서는 사용할 수 없으므로, 함수의 prototype에도 참조 추가한다. 단 이러면 각 사용시
- 객체 상수 : 상수는 보통 명명규칙으로 해결. 모두 대문자. 객체의 상수 프로퍼티는 생성자 함수의 프로퍼티로 정의. 책에 상수 정의하는 함수 있는데... 명명 규칙으로 해결하는게 방법일듯.
- 체이닝 패턴 : 메소드를 호출한 결과가 자기 객체를 반환하게 해서 메소드 호출을 계속할 수 있게 함.
- 체이닝 패턴의 장단점 : 코드가 짧아지고 읽기 좋다. 단일 작업을 하는 작은 함수를 만들게 유도한다. 디버깅 하기 힘들다. jQuery에서 사용하는 패턴
- method() 메소드 :
prototype의 프로퍼티로 함수를 추가하는 syntatic sugar. 구현은 책 참조
6장 코드 재사용 패턴
- 클래스 방식 vs 새로운 방식의 상속 패턴 : Java 방식의 상속을 클래스 방식이라고 하고, 그 외의 것들을 모두 새로운 방식이라고 하자. 자바 방식은
Person p = new Person();. 유사하게 JavaScript에서는var p = new Person();로 가능. - 클래스 방식의 상속을 사용할 경우 예상되는 산출물 :
function Parent(name) { }; function Child(name) { }; inherit(Child, Parent);에서 inherit 함수가 상속 구조를 만든다. - 클래스 방식의 상속 패턴 #1 - 기본 패턴 : Parent의 인스턴스를 Chld의 prototype으로 설정한다.
- 클래스 방식의 상속 패턴 #1 - 기본 패턴 :
- 클래스 방식의 상속 패턴 #1 - 기본 패턴 :
- 클래스 방식의 상속 패턴 #1 - 기본 패턴 :
- 클래스 방식의 상속 패턴 #1 - 기본 패턴 :
- 클래스 방식의 상속 패턴 #1 - 기본 패턴 :
