Skip to content

Latest commit

 

History

History
277 lines (188 loc) · 8 KB

Variable.md

File metadata and controls

277 lines (188 loc) · 8 KB

자바스크립트의 변수

자바스크립트의 실행

자바스크립트가 브라우저에서 해석될 때, 해당 자바스크립트가 실행되기 위한 최소한의 메모리를 확보하는 과정을 거친다. 이 과정에서 자바스크립트 변수는 호이스팅 과정을 거친다.

그렇다면 변수가 선언됐을 때 자바스크립트에서 어떤 과정이 일어나는지 살펴보자.

선언

값을 할당하지 않고 선언만 하는 과정을 거친다.

var a;에서 자바스크립트 엔진은 데이터의 공간을 만들고 그 공간에 a라는 이름을 붙인다.

초기화

이 단계에서 변수는 undefined로 초기화된다.

할당

undefined로 초기화된 변수에 값을 할당하는 과정이다. var a = '';에서 a라는 변수에 ''를 할당한다고 한다.

let

호이스팅

이쯤에서 변수의 호이스팅(Hositing) 의 개념을 알고 넘어갈 필요가 있다.

이전에 스코프에 대해 간략히 설명하면 var를 이용하여 선언한 자바스크립트의 변수는 기본적으로 함수형 스코프를 가진다. 하지만 ES6에서부터는 letconst를 이용하여 블록 레벨 스코프를 사용할 수 있다.

자바스크립트는 변수의 선언부를 해당 스코프의 가장 위로 끌어올린다. 이는 함수형 스코프든 블록 레벨 스코프든 동일하다.

코드로 살펴보자.

console.log(x);

var x = 1000;

console.log(x);

/*
undefined
1000
*/

위의 출력 결과를 보면 가장 첫 줄의 console.log(x)는 에러가 나야할 것 같지만, undefined로 출력이 된다. 이는 자바스크립트에서의 호이스팅이라는 개념으로 인해 변수의 선언부가 최상위로 끌어올려져서 나타나는 현상이다. 즉, 자바스크립트에서는 위와 같은 코드를 다음과 같이 해석한다.

var x;

console.log(x);

x = 1000;

console.log(x);

여기서 많은 개발자들이 착각하고 있는 부분이 있다.

'let으로 선언된 변수는 호이스팅 과정을 거치지 않는다' 라는 것이다.

하지만 이것은 틀린 말이다. 자바스크립트 파일의 메모리를 할당하는 과정이 필요하기 때문에 호이스팅이 발생한다.

ES6

위 그림에서 보면 알 수 있듯이 let으로 선언되면 초기화 단계 이전에 TDZ(Temporal Dead Zone)에 빠지게 된다. 아래 예제를 살펴보자.

//ES5(var)
console.log(a); //undefined

var a = 1;

console.log(a); //1
//ES6(let)
console.log(b); //Reference Error: b is not defined

let b = 1;

console.log(b);

위의 예제에서 볼 수 있듯이 let으로 선언한 변수를 그 이전에 사용하려고 하면 Reference Error가 발생한다. 이를 보면 호이스팅이 발생하지 않는다고 보일 수 있다.

하지만, 호이스팅이 일어나 메모리 공간을 확보한 뒤, 일시적 사각지대(TDZ) 에 빠져있는 상태이기 때문에 Reference Error가 발생하는 것이다.

TDZ와 관련하여 좀 더 자세히 잘 설명해놓은 글을 함께 소개하도록 하겠다.

블록레벨 스코프

var함수레벨 스코프라면 let블록레벨 스코프를 지원한다.

즉, var로 선언한 변수는 함수 내에 선언하지 않고 iffor와 같은 블록 내에서 선언하면 전역(window) 스코프에 할당된다.

반면, let으로 선언한 변수는 iffor와 같은 블록 내에서 선언을 하면 전역이 아닌 해당 블록 내의 스코프에 할당된다.

//ES5(var)
if(true) {
    var result = 'helloWorld';
}

console.log(result); //helloWorld
//ES6(let)
if(true) {
    let result = 'helloWorld';
}

console.log(result); //ReferenceError: result is not defined

if문 내에서 let으로 선언한 변수는 var와 다르게 블록 내의 지역 변수로 할당 되기 때문에 블록 외부에서는 이를 호출하면 Reference Error가 발생하게 된다.

변수 중복 선언 불가

let으로 선언한 변수는 중복 선언이 불가하다.

var로 선언한 변수는 같은 레벨에서 재선언이 가능하지만, let으로 선언한 변수를 같은 레벨 내에서 다시 선언하면 에러가 발생한다.

//ES5(var)
var result = 'hello';
var result = 'world';

console.log(result); //world
//ES6(let)
let result = 'hello';
let result = 'world'; 

//Uncaught SyntaxError: Identifier 'result' has already been declared

클로저(Closure)

var를 사용할 때 보다 let을 사용할 때 직관적이고 편하다고 생각할 수 있는 대표적인 예가 루프안에서 클로저를 구현할 때다.

//ES5(var)
function count(number) {
    for(var i=1; i <= number; i++) {
        (function (j) {
            setTimeout(function(){
                console.log(j);
            }, i*1000)
        }(i))
    }
}

count(4);
/*
1
2
3
4
/*

즉시 실행 함수를 실행시켜 루프의 i 값을 j에 복사하고 setTimeout()함수에서 사용했다. 이 때 j는 상위스코프의 자유변수이므로 그 값이 유지된다.

위처럼 구현하는 이유는

function count(numberOfCount) {
    for(var i=1; i <= numberOfCount; i++) {
        setTimeout(function(){
            console.log(i);
        }, i*1000)
    }
}

count(4);
/*
5
5
5
5
*/

이와 같이 구현했을 때 결과는 예상과 다르게 5가 4번 1초 간격으로 출력되기 때문이다.

이는 for 루프의 초기문에서 사용된 변수는 함수 레벨 스코프로 인해 전역 변수로 할당되기 때문에 문제가 발생하기 때문이다.

//ES6(let)
function count(numberOfCount) {
    for(let i=1; i <= numberOfCount; i++) {
        setTimeout(function(){
            console.log(i);
        }, i*1000)
    }
}

count(4);
/*
1
2
3
4
*/

하지만 let블록 레벨 스코프를 가지기 때문에 변수 i는 for문 블록 내의 지역 변수로 할당이 된다.

따라서 위와 같이 간단하게 원하는 결과를 출력할 수 있다.

const

재할당 불가

const는 상수 즉, 변하지 않는 값을 선언하기 위해 사용한다.

이에 따라 const로 선언된 변수는 varlet으로 선언된 변수처럼 재할당이 불가능하다.

const FOO = 123;

FOO = 234;
//Uncaught TypeError: Assignment to constant variable.

위 처럼 const로 선언된 변수에 다른 값을 재할당하려하면 에러가 발생한다.

블록레벨 스코프

const로 선언된 변수 또한 let으로 선언된 변수처럼 블록 레벨 스코프를 가진다.

객체의 할당

const로 선언된 변수는 위에서 말한 것처럼 재할당이 불가능하다.

하지만, const로 선언된 변수의 값이 객체로 할당이 된 경우, 객체 자체를 재할당하는 것은 불가능하지만 객체의 프로퍼티 값은 보호되지 못한다.

const Developer = {
    name : 'BKJang',
    age : 25,
    lang : 'Javascript'
}

Developer.lang = 'Java';

console.log(Developer); //{name: "BKJang", age: 25, lang: "Java"}

Developer = {
    name : 'BKJang',
    age : 25,
    lang : 'Java'
}
//Uncaught TypeError: Assignment to constant variable.

위 내용들을 살펴보았을 때, letconst가 있는데 왜 var가 존재하는지 의문이 생길 수 있을 것 같다.
letconstvar가 가지고 있던 단점들을 보완하기 위해 ES6(ECMA2015)부터 등장하게 된 새로운 문법이며 새로운 개발을 진행하게 될 때 var를 사용할 일은 아마도 없을 것 같다.

Reference