minzzl

[Javascript] 데이터타입 let var const 본문

프로젝트/자바스크립트

[Javascript] 데이터타입 let var const

minzzl 2022. 10. 7. 11:36
728x90
반응형

 

우선 Javascript의 데이터타입은 두가지로 나눌 수 있습니다.

변경이 가능한 Mutable 데이터타입, 변경이 불가능한 Immutable 데이터타입이 존재합니다.

 

그렇다면 Mutable 데이터타입을 먼저 알아보겠습니다.

 

var

  • 변수 중복 선언 가능
  • hoisting
  • No Block-level Scope
  • Function-level scope 
  • var 키워드 생략 가능

 

변수 중복 선언

var x = 1; 
 var x =50;
 var x; // 이때는 값을 할당하지 않았으므로, 
 console.log(x);

var는 변수를 중복해서 선언 및 초기화가 가능하다. 만약 사용자가 동일한 이름의 변수가 이미 선언되었다는 것을 모르고 의도치 않게 중복 선언을 하고 값을 할당한다면, 원래 의도와는 다른 값이 출력될 수 있습니다.

 

호이스팅

 // 이때 변수 age는 undefined 로 초기화된다.
 console.log(age); //undefined
 age = 50;
 console.log(age); // 50
 var age;

우선 호이스팅이란 어디에 선언했는지는 상관 없이 항상 제일 위로 선언을 끌어올려 주는 것을 의미합니다. 조금 더 구체적으로 말하자면 스코프 안에 있는 선언들을 모두 스코프의 최상위로 끌어올리는 것을 의미합니다. 이는 Javascript 인터프리터가 코드를 해석할 때 함수의 선언, 할당, 실행을 모두 나눠서 처리하기 때문입니다.

 

console.log(name) // undefined
var name = 'hello'

 이 코드는 참조에러가 나지 않고, undefined를 리턴합니다.

그 이유는 Javascript가 호이스팅을 하면서 아래와 같은 방식으로 코드를 해석하기 때문입니다.

var name // undefined
console.log(name)
name = 'hello'

 

선언단계에서 스코프에 변수 식별자를 등록하여 Javascript 엔진에 변수의 존재를 알립니다. 그렇기 때문에 초기화 단계에서 undefined 로 변수를 초기화 하게되고 변수 선언문 전에 변수에 접근해도 이미 스크프에 변수가 존재하기 때문에 에러가 발생하지 않고 undefined로 초기화한 변수를 반환하게 됩니다.

 

No Block-level Scope

{
    var age;
    age = 5;
}
age = 6
console.log(age);

블럭 안에 선언된 변수는 블럭 안에서만 사용이 가능하지만, var의 경우 블럭 안에 선언된 변수임에도 블락 밖에서도 사용이 가능합니다.

 

Function-level scope 

var name = 'hello'

function t() {
  var name = 'hi'
  console.log(name) // hi
}

t()
console.log(name) // hello

대부분의 프로그래밍 언어들은 Block-level scope를 사용하고 있습니다. 그러나 var의 경우 함수 레벨 Scope를 갖는데요, 이 때 Block-level scope란 Block 내에서 유효한 scope를, function-level scope 란 함수 코드 블록 내에서만 유효하고 함수 외부에서는 유효하지 않다는 것입니다. 이 때 유효하다는 것은 참조 혹은 접근이 가능하다는 것을 의미합니다.

 

var 키워드 생략 가능

a = 1;
console.log(a);

위와 같이 var 키워드 없이 a라는 이름의 변수를 선언 및 초기화해서 사용할 수 있습니다.

 

하지만 var라는 키워드 없이 선언하면, 함수가 선언한 환경의 this에 영향을 받습니다. 이는 일반적인 웹 환경에서는 window 일것이며 자바스크립트의 window 내장 객체가 값을 전역으로 관리하게 되고 해당 값이 덮어 씌워져 예상과 다르게 동작할 수 있으니 조심해서 사용해야합니다.

a = 3;

function add() {
  a = 5;
  console.log(a);
}

add(); // 5
console.log(a); // 5
var a = 3;

function add() {
  var a = 5;
  console.log(a);
}

add(); // 5
console.log(a); // 3

 

let

  • 변수 중복 선언 불가능
  • hoisting
  • Block-level Scope
  • let 키워드 생략 가능

 

변수 중복 선언 불가능

let x = 1; 
let x =50; //err
  
 console.log(x);

let은 var와는 달리 변수 중복 선언이 불가능합니다. 이름이 같은 변수를 중복으로 선언한 경우 문법에러가 발생합니다.

 

hoisting

console.log(x); // throws a ReferenceError
let x = 'hey';

위 예제를 보면, let 선언은 호이스팅을 수행하지 않는다고 생각할 수 있습니다. 그러나 호이스팅이 일어납니다. 이것을 이해하기 위해서는 Temporary Dead Zone 에 대해 알아야하는데요, 아래의 코드를 통해 살펴보겠습니다.

 

name = 'hello' // ReferenceError: Cannot access 'name' before initialization
let name = 'hi'

만약 name이 호이스팅 되지 않았다면, 두 번째 let선언에서 already declared에러가 났었어야 합니다. 그러나 초기화 하기전에 엑세스 할 수 없다는 에러를 내뱉었습니다.

 

그렇다면 아래의 코드를 살펴봅시다.

function sayHello() {
  return name
}
let name = 'hi'
console.log(sayHello()) // hi

아무런 문제 없이 hi가 출력되었는데요, 즉 name 호이스팅 되었다는 것입니다.

 

Javascript는 총 3단계에 걸쳐서 변수를 생성합니다.

1. 선언(Declaration) : 스코프와 변수 객체가 생성되고, 스코프가 변수 객체를 참조.

2. 초기화(Initialization) : 변수 객체 값을 위한 공간을 메모리에 할당. 이 때 할당되는 값은 undefined 이다.

3. 할당(Assignment) : 변수 객체에 값을 할당.

위에서 var은 선언과 동시에 초기화가 이루어집니다. 즉 선언과 동시에 undefined가 할당됩니다.

 

그러나 let은 다릅니다. 선언만 될뿐, 초기화가 이루어지지 않습니다. 바로 이 단계에서 TDZ에 들어가게 되는 것입니다. 즉 선언은 되었지만, 초기화가 되지 않아 이를 위한 자리가 메모리에 준비되어있지 않은 상태라는 것입니다. 이와 같이 tdz가 필요한 이유는 동적언어이다 보니깐 runtime type check 가 필요하기때문입니다.

 

TDZ 란 Temporal Dead Zone 의 약자로 우리 말로 번역하면 일시적 사각지대라는 의미이며, 스코프 시작 ~ 초기화 시작 사이의 구간을 의미합니다. 다른 말로 변수가 선언되고 변수의 초기화가 이루어지기 전까지의 구간이라고 말할 수 있겠습니다.

 

Block-level Scope

{
  {
    {
      var name = 'hello'
    }
  }
}
console.log(name) // hello

{
  {
    {
      let name2 = 'hi'
    }
  }
}
console.log(name2) // ReferenceError: name2 is not defined

Block-level scope는 블록 내부에서 선언한 변수는 모두 지역변수로 취급됩니다. 때문에 블록 내부에서 선언한 변수들을 참고할 수가 없습니다.

 

let 키워드 생략 불가능

 

let 키워드를 생략하면 var 처럼 작동하게됩니다.

 

 

.

.

.

 

그렇다면 Immutable 데이터타입을 알아보겠습니다.

 

const의 경우 let 에서 언급한 특징들을 모두 포함하고 있습니다. 따라서 아래에서는 const에서만 구별되는 특징만 몇가지 짚고 넘어가도록 하겠습니다.

const

1. 초기화와 동시에 선언이 이루어져야한다.

위에서 Javascript에서 변수는 3단계, (1)선언 (2)초기화 (3)할당 을 거쳐 생성된다고 언급했습니다. var의 경우 선언과 동시에 초기화가 일어나고 let은 선언만 될 뿐 초기화가 이루어지지 않습니다. 그렇다면 const는 어떨까요? const 는 선언과 동시에 초기화, 할당까지 이루어집니다.   따라서 아래의 코드에서 호이스팅이후 초기화가 진행되지 않았기 때문에 const 변수에서 다음과 같은 에러가 발생합니다.

let hello
hello = 'hello'

const hi //SyntaxError: Missing initializer in const declaration
hi = 'hi'


2.const  자체가 값을 불변으로 만드는 것은 아니다.

 

const는 변수 재선언, 재할당 모두 불가능하지만 값 자체를 불변으로 만드는 것은 아닙니다.

 

우선 아래의 코드는 당연히 불가능 합니다. 

const hello = 'hello'
hello = 'hi' // TypeError: Assignment to constant variable.

const 변수는 재 할당이 불가능하기 때문입니다.

 

 

 

그러나 아래의 코드들을 한번 살펴봅시다.

const hello = ['hi']
hello.push('hello')

const는 선언과 동시에 할당이 이루어질 때 사용하는 것으로, 재할당이 불가능하다는 특징이 있습니다.
push(), pop()의 경우 배열을 다른 값으로 교체하는 것, 재할당하는 것이 아닌, 기존 배열 원본을 수정하는 것이므로 위의 코드가 잘 동작합니다. 배열은 포인터로 선언이 되어있는데, push, pop은 포인터를 바꾸는 것이 아닌 같은 포인터의 값만 변경시키는 것입니다.

 

또한 아래의 코드도 다음과 같이 작동할 수 있습니다.

const hello = 'hello'
var hi = hello
hi = 'hi'
console.log(hello) // hello
console.log(hi) // hi


3. 상수를 선언할 때 사용한다.

재할당이 필요없는 경우 const를, 재할당이 필요하다면 let 사용을 하는 것이 좋다. 

let으로 선언되어 있다면, 어디선가 이 변수가 바뀔지도 모른다는 생각을 가지고 있어야 하므로, 코드를 읽기가 어려워지만 const는 초기화, 선언, 할당까지 되어 있으니 변경되지 않을 것이다라는 확신으로 코드를 볼 수 있습니다.

 

 

 

* 아래의 글들을 참고하여 작성하였습니다.

 

https://yceffort.kr/2020/05/var-let-const-hoisting

https://ddb8036631.github.io/javascript/var,-let,-const-%ED%82%A4%EC%9B%8C%EB%93%9C/

728x90
반응형