본문 바로가기
Web Frontend/JavaScript

[ JavaScript ] var와 let의 유효범위 및 스코프 동작의 차이

by quessr 2023. 5. 21.

 "var를 사용하지말고 let•cosnt를 사용해라."는 말은 코딩공부를 시작하면서 많이 들었던 조언 중 하나였다.
그러나 var를 사용하지 말아야 하는 이유에 대해 깊게 이해하지 않고 그냥 사용하지 않았던거 같다.

그런데 '자바스크립트의 코딩의기술' 책을 읽으면서 var와 let의 유효범위로 인한 차이점에 대한 설명을 보게 되었다.
읽다보니 스코프와 관련해서 아주 중요한 개념이 담겨있다는 생각이 들었다.

그 예시를 이해하기 쉽게 정리 해 두고자 한다.

 

var로 할당한 변수
let으로 할당한 변수

위 두 로직을 보면 for문 내 변수 i를 var로 할당했는지 let으로 할당했는지에 따라 결과값이 다르는게 나오는것을 확인 할 수 있다.
왜 이런 결과가 나오는 것일까?
결론적으로 var와 let의 유효범위가 다르기 때문이다.
var는 함수스코프를 따르고, let은 블로스코프를 따르는 변수이다.

위 내용을 바탕으로 한줄한줄 코드를  읽어나가며 변수의 유효범위가 결과값에 어떻게 영향을 미치게 된건지 확인 해 보도록 하자.

1. let으로 선언된 변수인 경우

function addClick(items) {
  for( let i = 0; i < items.length; i++) {
    items[i].onClick = function () {
      return i
    }
  }
  return items
}

const example = [{}, {}]
const clickSet = addClick(example)
clickSet[0].onClick()
clickSet[1].onClick()

let으로 선언한 변수의 유효범위 (블로 스코프)

clickSet에 addClick함수를 할당 한 뒤, example를 인자로 담아서 호출 한다.

function addClick([{}, {}]) {
  for( let i = 0; i < example.length; i++) {
    items[i].onClick = function () {
      return i
    }
  }
  return example
}

const clickSet = [ { onClick: () => return i }, { onClick: () => return i } ]

const clickSet에는 onClick을 key로 갖고 return i를 하는 함수를 값으로 갖는 객체를 가진 배열이 할당된다.

인자로 들어간 example의 길이가 2이므로, 반복은 총 2번 돌게되며
첫번째 반복문에서의 함수는 return i의 값이 0,
두번째 반복문에서의 함수는 return i의 값이 1 이 되고 반복문이 종료된다.

let으로 선언된 변수는 블록 스코프를 갖기 때문에
for문의 중괄호 블록내에서만 유효하게 된다.
각각의 반복문 실행마다 새로운 스코프를 생성되며 변수가 갱신된다.
처음부터 끝까지 i의 값은 반복문 실행에 따라 순차적으로 변경되며, 마지막 반복문 실행 후에는 조건을 충족하지 못하여 블록스코프 내부로 진입하지 못하고 소멸하게 된다.

따라서,
clickSet[0].onClick()의 값은 0
clickSet[1].onClick()의 값은 1 이 된다.

2. var로 선언된 변수인 경우

function addClick(items) {
  for( var i = 0; i < items.length; i++) {
    items[i].onClick = function () {
      return i
    }
  }
  return items
}

const example = [{}, {}]
const clickSet = addClick(example)
clickSet[0].onClick()
clickSet[1].onClick()

var로 선언한 변수의 유효범위 (함수 스코프)

 

function addClick([{}, {}]) {
  for( var i = 0; i < example.length; i++) {
    items[i].onClick = function () {
      return i
    }
  }
  return example
}

const clickSet = [ { onClick: () => return 2 }, { onClick: () => return 2 } ]


인자로 들어간 example의 길이가 2이므로, 반복은 총 2번 돌게되고
첫번째 반복문에서의 함수는 return i의 값이 2,
두번째 반복문에서의 함수는 return i의 값이 2 이 되고 반복문이 종료된다.

위의 let으로 선언된 변수와 다르게 var로 선언된 변수는 함수 스코프를 갖는다.
var로 선언된 변수는 반복문 내에서 선언되어도 반복문 블록 외부에서도 유효하게 되기 때문에
반복문이 실행될 때마다 i변수의 값이 갱신되며 최종적으로 2번의 반복(i = 0, i = 1)을 돌고 i의 값이 하나더 증가되어 2가 된다.
이 값은 addClick 함수의 스코프 내에서 유지되며, onClick 함수가 호출될때 i의 값인 2가 반환된다.

따라서,
clickSet[0].onClick()의 값은 2
clickSet[1].onClick()의 값도 2가 된다.

3. 정리

var로 선언된 변수는 함수 스코프를 갖게 된다.
이는 반복문이나 조건문과 같은 블록 내부에서 선언해도 함수 전체에서 접근할 수 있다. 
이로 인해 반복문에서 var로 선언한 변수를 사용하면 반복이 진행되면서 변수 값이 덮어씌워지는 문제가 발생 할 수 있다.

반면, let과 cosnt는 블록 스코프를 갖는다.
이는 변수가 선언된 블록 내에서만 유효하게 되며, 블록 외부에서는 접근할 수 없다.
따라서 반복문 내에서 let으로 선언한 변수는 각 반복만다 새로운 변수가 생성되어 값을 유지하게 된다.


이러한 차이로 인해 var를 사용하면 의도치 않게 변수 값이 덮어씌워지는 등의 예기치 않은 동작이 발생할 수 있다.
따라서 var보다는 let과 const를 사용하는 것이 좋다.
let은 재할당이 가능한 변수를, const는 재할당이 불가능한 상수를 선언하는데 사용된다.
이를 통해 변수의 범위를 명확하게 지정하고 예기치 않은 버그를 방지 할 수 있다.


참고: 자바스크립트의 코딩의기술