오래된 var

var는 블록 스코프가 없다.

var로 선언한 변수의 스코프는 함수 스코프이거나 전역 스코프이다.

if (true) {
  var test = true; // 'let' 대신 'var'를 사용했습니다.
}

alert(test); // true(if 문이 끝났어도 변수에 여전히 접근할 수 있음)
//(블록 밖에서도 여전히 접근 가능)

var는 코드 블록을 무시하기 때문에 test는 전역 변수가 된다. 전역변수를 어디서는 접근하는 것은 이상한 일이 아니다..! 다만 let이었다면 접근하지 못했을 것이다.

ar는 블록이나 루프 수준의 스코프를 형성하지 않는다.

for (var i = 0; i < 10; i++) {
  // ...
}

alert(i); // 10, 반복문이 종료되었지만 'i'는 전역 변수이므로 여전히 접근 가능합니다.

단, 코드 블록이 함수 안에 있다면, var는 함수 레벨 변수가 된다.

function sayHi() {
  if (true) {
    var phrase = "Hello";
  }

  alert(phrase); // 제대로 출력됩니다.
}

sayHi();
alert(phrase); // Error: phrase is not defined

렉시컬 환경( == 블럭별로 쌓임.) 개념이 없던 시절에 만들어졌으므로, 함수와 동일한 스코프를 가진다. (variable environment에 들어간다.)

var는 변수의 중복 선언을 허용한다.

한 스코프에서 같은 변수를 let으로 두 번 선언하면 에러가 발생하지만, var는 가능하다.

선언하기 전 사용할 수 있는 var

var 선언은 함수가 시작될 때 처리된다. 전역 변수라면 스크립트가 시작될 때 처리된다. (let, const는 TDZ에 들어가 선언 문장을 만나기 이전에는 접근이 불가능하다.)

함수 본문 내에서 var로 선언한 변수는 선언 위치와 상관없이 함수 본문이 시작되는 지점에서 정의된다.(단, 변수가 중첩 함수 내에서 정의되지 않아야 이 규칙이 적용됩니다).

따라서 아래 두 예제는 동일하게 동작합니다.


//1
function sayHi() {
  phrase = "Hello";

  alert(phrase);

  var phrase;// 선언문이 가장 아래에 나왔지만 올바르게 동작한다. 마치 선언문이 가장 먼저 나온 것 처럼
}
sayHi();

//2
function sayHi() {
  var phrase;

  phrase = "Hello";

  alert(phrase);
}
sayHi();

코드 블록은 무시되기 때문에, 아래 코드 역시 동일한 동작을 한다.