이 글은 "JavaScript Patterns" 책을 공부하며 요약해 쓴 글입니다.
for
for 루프는 배열, arguments, HTMLCollection 등 배열과 비슷한 객체를 순회한다.
*HTMLCollection: HTML 요소들의 리스트로 유사배열객체이다.
ex) var x = document.getElementByTagName('div');
// getElementByTagName 메서드가 div 요소들의 리스트인 HTMLCollection을 반환한다.
// 변수 x에 담긴 HTMLCollection 객체는 인덱스 번호로 접근할 수 있다.
var y = x[1];
일반적인 for 문
xxxxxxxxxx
for(var i=0; i < arr.length; i++) {
// arr[i]를 다루는 코드
}
이 패턴은 루프 순회시마다 arr의 length에 접근한다.
만약 arr이 배열이 아닌 HTMLCollection이라면 코드가 느려질 수 있다.
- 실제 DOM의 접근 비용은 일반적으로 크다.
- HTMLCollection 객체의 length 속성 접근은 실제 DOM에 질의를 요청하는 것과 같다.
- 실제 DOM 질의는 실시간으로 처리된다. 브라우저는 DOM 조작이 일어나면 다시 브라우저에 렌더링하는데에 수 많은 처리 과정을 거치는데, 이는 많은 비용이 요구된다. (reflow를 참고)
arr.length의 값이 10000이라면 브라우저를 10000번 렌더링하는 것과 같다.
여러 최적화 방법들
1. 배열(또는 콜렉션)의 length를 캐시
xfor(var i=0, length=arr.length; i<length; i++) {
// here code
}
// 단일 var 패턴
function test() {
var i,
length,
arr = [];
for(i=0, length=arr.length; i<length; i++) {
// here code
}
}
HTMLCollection 객체의 length 값을 초기화 변수에 저장하여 한 번의 DOM 접근만이 이루어진다.
2. 변수 갯수 줄이기, 배열을 거꾸로 순회
xxxxxxxxxx
var i, arr = [];
for(i=arr.length-1; i<0; i--) {
// here code
}
length 변수를 없애고, 배열의 카운트를 감소시켜 0으로 내려간다. (미세 최적화)
- 0과 비교하는 것이 배열의 length 또는 0이 아닌 값과 비교하는 것보다 대게 더 빠르다.
- 성능이 결정적인 요소가 되는 작업에서 사용하는 것을 권장
for-in
for-in 루프는 배열이 아닌 객체를 순회할 때 사용해야 한다.
- 자바스크립트의 배열은 객체이기 때문에 for-in 루프를 통해 순회할 수 있지만, 권장사항이 아니다.
- 배열 객체에 사용자가 정의한 기능이 추가되면 논리적인 오류가 발생할 수 있다.
- 열거 순서가 정해져있지 않다. 실제로 배열은 for-in이 아닌 for 루프로 순회하는 것이 좀 더 빠르다.
xxxxxxxxxx
var fruits = {
apple: 3,
tomato: 1,
grape: 2
};
for(var i in fruits)
console.log(fruits[i]);
일어날 수 있는 오류 피하기
코드 내에 'Object.prototype.메서드명'이라는 사용자가 지정한 메서드가 모든 객체에 추가되었다고 가정하자.
이 메서드 프로퍼티는 프로토타입 체인을 따라 상속되어 모든 객체의 프로퍼티로 사용할 수 있게 된다.
이 문제로 인해 어떤 객체가 for-in 루프로 순회하는 과정에서 의도치 않게 메서드가 사용될 수 있다.
ex)
xxxxxxxxxx
var fruits = {
apple: 3,
tomato: 1,
grape: 2
};
// helloWorld 메서드 추가
Object.protype.helloWorld = function() {
console.log('Hello World!');
};
for(var i in fruits)
console.log(fruits[i]);
// 출력
3
1
2
f () {
console.log('Hello World!');
}
1. hasOwnProperty() 사용
Object의 hasOwnProperty 메서드를 사용하여 객체 변수의 고유 프로퍼티만을 사용할 수 있다.
xxxxxxxxxx
for(var i in fruits) {
if(fruits.hasOwnProperty(i)) {
console.log(fruits[i]);
}
}
// 출력
3
1
2
2. Object.prototype.hasOwnProperty.call() 사용
이 패턴은 만약 fruits 객체의 hasOwnProperty 메서드를 오버라이딩 했을 경우에도, 온전한 hasOwnProperty 메서드를 사용할 수 있게 해준다.
xxxxxxxxxx
console.log(fruits.hasOwnProperty('apple')); // true
// hasOwnProperty 메서드 오버라이딩
fruits.hasOwnProperty = function() {
console.log("overwriting this method");
}
console.log(fruits.hasOwnProperty()); // overwriting this method
// 더 이상 fruits 객체의 hasOwnProperty를 통해 프로퍼티 존재를 알 수 없으므로
for(var i in fruits) {
if(Object.prototype.hasOwnProperty.call(fruits, i)) {
console.log(fruits[i]);
}
}
// 출력
3
1
2
3. Object.prototype.hasOwnProperty 캐시
프로퍼티 탐색이 체인을 따라 Object까지 거슬러 올라가지 않게 하기 위해, 지역 변수를 사용하여 메서드를 캐시한다.
xxxxxxxxxx
var i,
hasProperty = Object.prototype.hasOwnProperty;
for(i in fruits) {
if(hasProperty.call(fruits, i)) {
console.log(fruits[i]);
}
}
hasOwnProperty()를 사용하지 않았다고 해서 오류가 발생하지는 않으며, 루프 속도가 약간 감소될 수 있지만, 객체와 객체 프로토타입 체인의 내용을 보장할 수 없다면 hasOwnProperty()로 확인하는 것이 좀 더 안전한 방법이다.
'JavaScript' 카테고리의 다른 글
자바스크립트 : 좋은 코드를 위한 전역 변수의 최소화 (0) | 2017.12.31 |
---|