호이스트(Hoist: 끌어 올리다)현상 현재 영역(Scope)의 최상단으로 변수 이름 또는 함수 선언문이 끌어올려지는 것을 말한다.

1. 변수 범위 (Variable Scope)

변수 범위는 변수가 존재하는 컨텍스트이다. 이것은 어디에서 변수에 접근할 수 있는지, 그 컨텍스트에서 변수에 접근할 수 있는지를 명시적으로 나타낸다. 변수는 지역 범위(local scope)와 전역 범위(global scope) 둘 중 하나를 가진다.

1-1. 지역 변수 (함수 수준 범위)

대부분의 프로그래밍 언어와 달리, 자바스크립트는 블럭-수준(block-level)의 범위를 가지고 있지는 않다. 대신, 자바스크립트는 함수-수준(function-level)의 범위를 가진다. 함수내에 정의된 변수는 지역 범위를 가지며, 해당 함수와 내부 함수에서만 접근이 가능하다.

//함수-수준 범위의 예제
var name = "Richard";

function showName() {
     var name = "Jack"; // 지역 변수; showName()함수에서만 접근가능.
     console.log(name); // Jack
}
console.log(name); // Richard : 전역 변수
//잘못된 예제. (블럭-수준 범위로 오해할 경우)
var name = "Richard";
// 아래의 if문은 name변수에 대한 지역-범위를 생성하지 않는다.
if (name) {
     name = "Jack";
     console.log(name); // Jack : 전역 변수
}
// name은 여전히 전역변수이며 if문에서 변경됨.
console.log(name); // Jack

변수 정의시 var 를 사용하는것이 좋은데 왜냐면 var를 쓰지 않을 경우 암묵적으로 전역객체에 변수를 만들기 때문! 정확히 전역객체에 속성을 만들기 때문에 오류가 생길 가능성이 크다.

// 지역변수를 var키워드로 선언하지 않았을 경우, 그것은 전역-범위(global-scope)가 된다.
var name = "Michael Jackson";

function showCelebrityName() {
     console.log(name);
}

function showOrdinaryPersonName() {
     name = "Johnny Evers";
     console.log(name);
}
showCelebrityName(); // Michael Jackson

// name 은 지역변수가 아니다 이것은 전역변수 name을 변경해 버린다.
showOrdinaryPersonName(); // Johnny Evers

// 이제 전역변수 name은 Johny Evers 더이상, 셀럽의 이름은 없다.
showCelebrityName(); // Johnny Evers

// 해결책은 지역변수 선언시 var 키워드를 사용하면 된다.
function showOrdinaryPersonName() {
     var name = "Johnny Evers"; // 이제 name은 항상 지역변수이며, 전역변수를 덮어쓰지 않는다.
     console.log(name);
}

지역번수는 함수내에서 전역번수보다 높은 우선순위를 가지게 되는데 만약, 같은 이름의 전역변수와 지역변수가 존재할 경우 이 변수를 함수내에서 사용한다면, 지역변수가 우선권을 갖게 된다.

var name = "Paul";
function users() {
     var name = "Jack";
     console.log(name);
}
users(); // Jack

1-2. 전역 변수

함수의 외부에서 선언된 모든 변수는 전역 범위(global scope)를 가진다. 브라우저에서, 전역 컨텍스트(또는 scope)는 window 객체를 가리킨다.

그러므로, 전역변수는 전체 어플리케이션에서 사용이 가능하다.

// 전역변수는 아래와 같이 선언
var myName = "Richard";
// 또는
firstName = "Richard";
// 또는
var name;
name;

모든, 전역 변수는 window객체와 연결이 된다. 그러므로, 아래와 같이 window객체를 통해 모든 전역 변수에 접근이 가능하다.

console.log(window.myName); // Richard
// 또는
console.log("myName" in window); // true
console.log("firstName" in window); // true

만약, 변수가 최초 선언 없이(var 키워드를 사용하여) 초기화 되었다면, 이 변수는 자동으로 전역 컨텍스트에 추가 된다.

function showAge() {
     // age는 전역 변수
     age = 90;
     console.log(age);
}
showAge(); // 90
// age는 전역 변수이므로, 이런식으로도 호출할 수 있다.
console.log(age); // 90

아래의 firtName은 둘다 전역 범위이다. 두번째, firstName은 {} 블럭으로 쌓여있지만, 자바 스크립트는 블럭단위 범위를 지원하지 않는다는 것을 기억하자.

var firstName = "Richard";
{
     var firstName = "Bob";
}
console.log(firstName); // Bob
for (var i=1; i<=10; i++) {
     console.log(i); // 1~10까지 출력
}

// 변수 i는 전역 변수입니다. 그러므로, 아래 함수 호출시 i는 for문에서 실행된 후 마지막 값을 가르키게 된다.
function aNumber() {
     console.log(i);
}

aNumber(); // 11

setTimeout 변수는 전역 범위에서 실행된다. setTimeout 안에서 선언된 모든 함수는 전역 범위에서 실행됩니다. 다음 예제를 주의해서 보자.

// setTimeout 함수내에서 사용된 "this"객체는 myObj가 아니라, window객체를 참조
var highValue = 200;
var constantVal = 2;
var myObj = {
     highValue: 20,
     constantVal: 5,
     calculateIt: function() {
          setTimeout(function() {
               console.log(this.constantVal * this.highValue);
          }, 2000);
     }
}

// 전역변수인 highValue와 constantVal을 사용하여 계산된다. 200*2.
myObj.calculateIt(); //400

전역 범위를 오염시켜서는 안된다. 가급적 전역 범위에 변수를 생성하는것을 피하도록 하자.

var firstName, lastName;
function fullName() {
     console.log("Full Name : " + firstName + " " + lastName);
}

// 개선코드
// 함수내에 선언함으로서 이것은 지역변수
function fullName() {
     var firstName = "Michael", lastName = "Jackson";
     console.log("Full Name : " + firstName + " " + lastName);
}
//fullName() 함수 역시 전역 범위

2. 변수 호이스팅(Variable Hoisting)

모든 변수선언은 호이스트 된다.

호이스트란, 변수의 정의가 그 범위에 따라 선언과 할당으로 분리되는 것을 의미한다. 즉, 변수가 함수내에서 정의되었을 경우 선언이 함수의 최상위로, 함수 바깥에서 정의되었을 경우는 전역 컨텍스트의 최상위로 변경된다.

변수의 선언이 초기화나 할당시에 발생하는것이 아니라, 최상위로 호이스트 된다는 것!

function showName() {
     console.log("First Name : " + name);
     var name = "Ford";
     console.log("Last Name : " + name);
}
showName();
// First Name : undefined
// Last Name : Ford
// First Name이 undefined인 이유는 지역변수 name이 호이스트 되었기 때문

function showName() {
     var name; // name 변수는 호이스트 되었다. 할당은 이후에 발생하기 때문에, 이 시점에 name의 값은 undefined 이다
     console.log("First name : " + name); // First Name : undefined
     name = "Ford"; // name에 값이 할당
     console.log("Last Name : " + name); // Last Name : Ford
}

호이스트 되었을때, 함수 선언은 변수선언을 덮어 쓴다.

// 다음 두 변수와 함수는 myName으로 이름이 같다.
var myName; // string

function myName() {
     console.log("Rich");
}
// 함수 선언은 변수명을 덮어 쓴다
console.log(typeof myName); // function


//하지만, 변수에 값이 할당될 경우에는 반대로 변수가 함수선언을 덮어 씌움
var myName = "Richard";

function myName() {
     console.log("Rich");
}

console.log(typeof myName); //string

“strict mode”에서 최초의 선언없이 변수에 값을 할당하려 한다면 오류가 발생한다. 변수에 값을 할당 하려 할때는 항상 미리 선언하는 습관을 들이자.