다루는 내용
- 고급 함수
- 쉽게 조작할 수 없는 객체
- 타이머 조작
- 커스텀 이벤트
1. 고급 함수
- 단순한 절차적인 방식, 복잡하고 동적인 방식, 클로저, 함수 포인터 등을 사용하는 다양성
1.1 안전한 타입 탐지
-
typeof 나 instanceof 처럼 내장된 타입 탐지는 완벽하지 않음
-
Object 의 toString() 메소드를 이용
1 2 3
Object.prototype.toString.call(value) == "[object Function]" window.JSON && Object.prototype.toString.call(JSON) == "[object JSON]"
1.2 스코프 확인 생성자 (scope-safe constructor)
-
new 연산자 없이 객체를 생성하면 this 가 전역에 묶여버리는 문제가 있음
1 2 3 4 5 6 7 8
function Person(name, age) { if(this instanceof Person) { this.name = name; this.age = age; } else { return new Person(name, age) } }
- if/else 를 통해 해결
-
생성자 훔치기 패턴을 사용할 때에는 프로토 타입 체인이나, 기생 상속을 함께 사용 자세한 내용은 (6장. 객체지향 프로그래밍 참조)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
function Polygon(sides) { if(this instanceof Polygon) { this.sides = sides; this.getArea = function() { return 0; }; } else { return new Polygon(sides); } } function Rectangle(width, height) { Polygon.call(this, 2); this.width = width; this.height = height; this.getArea = function() { return this.width * this.height; }; } Rectangle.prototype = new Polygon(); var rect = new Rectangle(5, 10); console.log(rect.sides); // 2
1.3 지연 로딩 함수
-
브라우저 차이 때문에 if 문이 많은데, if 문은 단 하나만 존재해도 속도에 영향을 줄 수 있기 때문에 if 문을 필요할 때만 실행하도록 하는게 권장되고 이를
지연로딩
이라고 함 -
- 함수를 처음 호출할 때 자기 자신을 수정하도록 만드는 방법
1 2 3 4 5 6 7 8 9 10
function createA() { if(typeof request != "undefined") { createA = function() { return new Request(); } } else { ... } return createA() }
- 적절한 함수를 선언하는 즉시 다른 함수를 할당하는 방법
1 2 3 4 5 6 7 8 9
var createA = (function() { if(typeof request != "undefined") { return function() { return new Request(); } } else { ... } })();
1.4 함수 바인딩
-
특정한 this 값과 특정한 매개변수를 넘기면서 다른 함수를 호출하는 함수
-
1 2 3 4 5 6 7 8 9
var handler = { message: "event", handleClick: function(event) { console.log(this.message + ":" + event.type); } }; var btn = document.getElementById("btn"); var bbtn = btn.bind(hadler);
1.5 함수 커링
-
커링은 단순히 감쌀 함수만 받지만, bind 는 함수와 context 객체를 함께 받음
1 2 3 4 5 6 7 8 9 10
function add(num1, num2) { return num1 + num2; } function curry(num3) { return add(5, num3); } add(2,3); // 5 curry(3); // 8
2. 쉽게 조작할 수 없는 객체
- 컨텍스트가 같으면 수정하지 못하게 막을 방법이 없음
- 일단 객체를 쉽게 조작할 수 없는 객체로 바꾸고나면 이를 취소할 수 없음
2.1 확장 불가능한 객체
- Ojbect.preventExtension() 를 통해 프로퍼티나 메소드를 추가할 수 없게 만듦
- 맴버를 수정하거나 삭제는 가능
- Object.isExtensible() 을 통해 확장 여부 확인 가능
2.2 봉인된 객체
- Object.seal() 메소드를 사용하며 [[configurable]] 속성이 false 가 됨
- Object.isSealed() 를 통해 봉인 여부 확인 가능
2.3 동결된 객체
- wirte 조차 불가능한 가장 강력한(?) 객체 ([[set]] 이 지정된 경우만 수정 가능 )
- Object.freeze() 를 통해 동결
3. 고급 타이머
- setTimeout() 이나 setInterval() 로 타이머를 생성하여 유용한 기능을 만들 수 있지만, 이들은 스레드이며 자바스크립트는 단일 스레드 환경에서 동작함
- 자바스크립트에서 즉시 실행되는 코드는 존재하지 않음. 모든 코드는 일단 큐에 추가되었다가 실행이 됨
- 타이머를 15초로 설정하면 코드가 15초 뒤에 실행되는 것이 아니라 15초 뒤에 큐에 추가 되는 것
3.1 타이머 반복
-
setInterval() 을 사용해 주기적으로 타이머 코드를 큐에 추가 가능
-
타이머 코드가 실행되기 전에 큐에 코드를 삽입할 가능성이 있기 때문에 다른 인스턴스가 큐에 존재하지 않을 때만 추가 하지만, 여기에는 2가지 단점이 있음
- 실행되지 않는 구간이 생길 수 있음
- 코드 사이의 갭이 예상보다 작을 경우 타이머 코드를 건너뛰게 됨
-
2번의 경우를 피하려면 setTimeout() 을 재귀호출 해야함
1 2 3 4 5 6
setTimeout(function() { // sth.. setTimeout(arguments.callee, 50); }, 50);
3.2 프로세스 관리
-
브라우저는 코드가 일정 시간 이상 실행되거나 일정 숫자 이상의 문장을 실행하지 못하도록
오래 사용되는 스크립트
를 제한 함 -
오래 실행되는 루프의 경우 배열 나누기를 통해 사용자 인터페이스가 잠기는 것을 방지할 수 있음
1 2 3 4 5 6 7 8
setTimeout(function() { var item = array.shift(); process(item); if(array.length > 0) { setTimeout(arguments.callee, 100); } }, 100)
3.3 함수 감속
-
짧은 시간 동안 DOM 조작을 너무 많이 하면 브라우저가 멈추기에 타이머를 이용해 감속으로 완화하는 목적
1 2 3 4 5 6
function throttle(method, context) { clearTimeout(method.tId); methid.tId = setTimeout(function() { method.call(context); }, 100); }
-
resize 같이 코드를 주기적으로 실행해야 하지만 호출 자체를 제어할 수 없을 때 사용
4. 커스텀 이벤트
- 이벤트는
옵저버
패턴의 일종인데, 코드를 느슨하게 연결하는 테크닉 - 주체는 이벤트가 일어났음을 알리고, 옵저버는 이들 이벤트를 주시하며 주체를 관찰하기만 함
- 기본 아이디어는 이벤트를 관리하는 객체를 만들고 다른 객체가 그 이벤트를 주시하게 하는 것
- 커스텀 이벤트를 사용하면 관련된 객체를 분리하고 기능을 격리하기 쉬움