도입
JavaScript에서 변수를 선언할 때 종종 마주치는 고민이 있다.
'for문 내부에 변수를 선언해도 될까? 아니면 for문 밖에서 선언하는 것이 더 좋을까?'
이런 고민이 생긴 이유는 let과 const는 var과 달리 재선언이 불가능하기 때문이다.
또한 'for문 내부에서 변수를 선언하면 반복이 될 때마다 선언되니 메모리적으로 비효율적이지 않을까? 혹시 문제가 발생하지 않을까?'라는 생각도 들었다(아래 코드의 2번 방식).
하지만 실제로 사용해보면 아무런 문제가 발생하지 않는다. 그래서 그냥 넘어갔던 문제인데, 이러한 고민이 들 때마다 찝찝한 마음이 들어서 이번 기회에 확실히 정리하고자 한다.
// 1. for 문 밖에서 변수를 미리 선언하고 재할당하는 방식
let ny, nx;
for (let i = 0; i < 4; i++) {
ny = y + dy[i];
nx = x + dx[i];
// ...
}
// 2. for 문 안에서 변수를 선언한 방식
for (let i = 0; i < 4; i++) {
const ny = y + dy[i];
const nx = x + dx[i];
// ...
}
결론부터 말하자면, for문 내부에서 변수를 선언해도 괜찮다! 그 이유를 하나씩 살펴보도록 하자.
JavaScript의 블록 스코프
for문 내부에서 변수 선언이 가능한 이유는 JavaScript의 블록 스코프 개념을 통해 알 수 있다.
JavaScript에서 let과 const로 선언된 변수는 블록 스코프를 가진다. 블록 스코프란 중괄호({})로 둘러싸인 영역 내에서만 변수가 유효한 범위를 의미하며, 변수의 사용 범위를 제한하여 코드의 유지보수를 쉽게 만든다. 간단한 예시를 통해 블록 스코프를 이해해보자.
{
let x = 10;
console.log(x); // 10
}
{
let x = 20;
console.log(x); // 20
}
위 코드에서 x는 각각 다른 블록에서 선언되었다. 따라서 서로 다른 변수로 취급되어 아무런 문제없이 실행된다.
for문도 마찬가지다. for문이 반복될 때마다 새로운 블록이 생성되고, 그 안에서 선언된 변수는 해당 블록 내에서만 유효하다.
for (let i = 0; i < 3; i++) {
const number = i * 10;
console.log(number);
}
// 출력: 0, 10, 20
각 반복마다 number는 새로운 블록 스코프에서 선언되므로, 이전 반복의 number와는 전혀 다른 변수이다.
그럼 성능에는 문제가 없을까?
for문이 반복될 때마다 새로운 변수를 선언하면 메모리에 변수가 쌓이니 성능이 안 좋아지지 않을까? 그러니 변수를 계속 새로 만드는 것보다는 재사용하는 게 더 효율적이지 않을까? 라는 생각이 들었다.
하지만 찾아보니 대부분의 경우에는 걱정할 필요가 없다. JavaScript의 최신 엔진들은 대부분 반복적인 변수 생성에 대한 최적화를 잘 수행하기 때문이다. 예를 들어, 반복적으로 생성되는 변수들을 메모리의 특별한 영역에 할당했다가 빠르게 정리하는 방식을 사용한다. 이런 최적화 덕분에 변수를 for문 밖에서 선언하든 안에서 선언하든 실제 성능 차이는 미미하다고 한다.
// 예시 1: 숫자 계산
for (let i = 0; i < 1000; i++) {
const sum = i + 1;
console.log(sum);
}
// 예시 2: 문자열 처리
for (let i = 0; i < 1000; i++) {
const message = `현재 숫자는 ${i}입니다`;
console.log(message);
}
위와 같이 숫자, 문자열과 같은 간단한 데이터를 다룰 때는 for문 안에서 변수를 선언해도 성능 차이가 거의 없다.
변수를 for문 밖에서 선언하는 것이 더 나은 경우
다만 다음과 같은 경우에는 변수를 for문 밖에서 선언하는 것이 더 나은 선택이다.
큰 배열이나 객체를 다루는 경우
// 비효율적인 방법
for (let i = 0; i < 1000; i++) {
const bigArray = new Array(10000).fill(0); // 매번 큰 배열을 새로 생성
bigArray[i] = i;
}
// 효율적인 방법
const bigArray = new Array(10000).fill(0); // 한 번만 생성
for (let i = 0; i < 1000; i++) {
bigArray[i] = i;
}
큰 배열을 매번 생성하면 메모리 사용량이 급격히 증가하여 성능에 문제가 생길 수 있다. 따라서 큰 배열은 한 번만 생성하고 재사용하는 것이 좋다.
DOM 요소를 다루는 경우
// 비효율적인 방법
for (let i = 0; i < 1000; i++) {
const element = document.createElement("div"); // 매번 새로운 DOM 요소 생성
element.textContent = i;
container.appendChild(element);
}
// 효율적인 방법
const element = document.createElement("div"); // 한 번만 생성
for (let i = 0; i < 1000; i++) {
element.textContent = i;
container.appendChild(element.cloneNode(true));
}
DOM 요소를 반복적으로 생성하는 대신, 한 번만 생성한 후 cloneNode를 사용하여 효율적으로 재사용할 수 있다.
복잡한 객체나 클래스를 다루는 경우
class ComplexCalculator {
constructor() {
// 복잡한 초기화 작업
}
calculate(number) {
return number * 2;
}
}
// 비효율적인 방법
for (let i = 0; i < 1000; i++) {
const calculator = new ComplexCalculator(); // 매번 새로운 객체 생성
const result = calculator.calculate(i);
}
// 효율적인 방법
const calculator = new ComplexCalculator(); // 한 번만 생성
for (let i = 0; i < 1000; i++) {
const result = calculator.calculate(i);
}
복잡한 객체를 반복적으로 생성하는 것은 불필요한 초기화 비용을 초래한다. 따라서 한 번만 생성하고 재사용하는 것이 좋다.
for문 안에 변수를 선언하는게 좋은 경우
반면, 다음과 같은 경우에는 for문 안에 변수를 선언하는 것이 좋다.
매 반복마다 새로운 값을 계산해야 하는 경우
for (let i = 0; i < coordinates.length; i++) {
const nextX = coordinates[i].x + dx[i];
const nextY = coordinates[i].y + dy[i];
// nextX, nextY는 매번 새로 계산되어야 하는 값
}
매 반복마다 다른 값을 계산해야 하는 경우에는 각 반복마다 새로운 변수를 선언하는 것이 더 명확하고 가독성이 좋다.
변수의 용도가 현재 반복에서만 의미가 있는 경우
for (let i = 0; i < words.length; i++) {
const reversed = words[i].split("").reverse().join("");
// reversed는 현재 단어를 뒤집은 결과로, 다음 반복과는 무관
}
변수가 반복 내부에서만 의미가 있을 때는 해당 블록 내에서만 변수를 선언하여 사용 범위를 제한하는 것이 바람직하다.
마무리
지금까지 살펴본 내용을 정리해보자.
- for문 내부에서 변수를 선언해도 된다.
- 이것이 가능한 이유는 블록 스코프 때문이다. 각 반복마다 새로운 블록이 생성되어 변수가 독립적으로 관리된다.
- 성능 면에서도 일반적인 경우에는 문제가 되지 않는다.
따라서 for문 내부에서 변수를 선언하는 것은 안전하며, 오히려 변수의 스코프를 명확하게 제한할 수 있어 코드의 가독성과 유지보수성을 높일 수 있다. 단, 대용량 데이터를 다루는 경우에는 상황에 맞게 적절한 판단이 필요하다.
참고 자료
'JavaScript' 카테고리의 다른 글
JavaScript에서 Number의 큰 수 처리 한계, BigInt로 해결 (2) | 2024.11.28 |
---|---|
데이터 타입은 왜 필요한가? 동적 타입 언어의 특징도 알아보자 (0) | 2024.08.02 |
JavaScript 원시 타입 종류 7가지 (0) | 2024.08.02 |
변수는 왜 필요한가?, 어떻게 선언되고 할당되는지도 알아보자 (feat. 메모리) (0) | 2024.07.31 |
스코프 체인 - 함수에서 어떤 값에 접근이 가능/불가능한가 따져보기 (0) | 2023.02.03 |