자바스크립트 JavaScript

변수의 타입과 Scope, Hoisting, 함수

leexx 2023. 1. 16. 08:00

타입

타입의 종류

  • 기본형 (Primitive type)
    • null
    • undefined
    • boolean
    • number
    • string
  • 복합형 (Non-Primitive type)
    • object
    • array

 

타입을 체크하는 방법

console.log(typeof a);

 

Undefined? Undefined(Undeclared)?

  • 두 변수들의 값을 출력하려고 한다. 이 때 선언이 되었지만 값이 없는 변수 a선언조차 되지 않은 변수 b가 있다.
    • var a; console.log(a); // undefined
    • console.log(b); // ReferenceError: b is not defined
    • b를 출력할 때 에러가 난다. 이해가 가는 내용이다.
  • 이어서 두 변수들의 타입을 출력하려고 한다. 이 때 선언이 되었지만 값이 없는 변수 a선언조차 되지 않은 변수 b가 있다.
    • var a;
      console.log(typeof a); // undefined
      
      console.log(typeof b); // undefined (undeclared)
    • 두개를 모두 출력하면 undefined 라는 값이 출력된다. a 야 정의되지 않은 것이 맞긴 하지만, 선언조차 되지 않은 b 또한 에러가 뜨지 않고 똑같이 undefined 를 출력한다.
    • 이 점을 활용하여 Reference Error의 안전 가드로 사용할 수 있다. 임의의 변수를 사용하려고 할 때, 이 변수가 선언이 아예 되지 않았는지, 또는 값이 없는지를 체크할 수 있다는 점에서 착안하였다.
    • if (typeof a === "undefined") console.log("변수가 올바르게 선언되지 않았습니다"); if (typeof b === "undefined") console.log("변수가 올바르게 선언되지 않았습니다");

 

JavaScript의 변수의 타입

  • (const를 제외하고) 변수를 선언한 이후 재 할당이 가능하다. 즉 변수가 가진 타입이 계속 바뀔 수 있다는 것이다.
  • 따라서 변수에 typeof 연산자를 대어보는 건 "이 변수의 타입은 무엇이니?" 라는 질문과 같지만, 실은 타입이란 개념은 변수에 없으므로 정확히는 "이 변수에 들어있는 값의 타입은 무엇이니?" 라고 묻는 것이다.

var, let, const의 차이

  • var과 let, const의 차이는 무엇일까?
    • scope가 다르다.
      • var : function-scoped
      • let & const : block-scoped
    • 재 선언, 재 할당 여부가 다르다.

 

 

Function scope, Block scope

  • Function scope
    • function scope를 갖는 var v 는 이처럼 함수 안에서만 존재한다.
  • function f() { var v = 5; console.log(v); // 5 } console.log(v); // ReferenceError: v is not defined
  • Block scope
    • block scope를 갖는 let a 는 이처럼 block 안에서만 존재한다.
  • for (let a = 0; a < 5; a++) { console.log(a); // 0 1 2 3 4 } console.log(a); // ReferenceError: a is not defined

 

var, let, const는 어디에 저장이 될까?

전부 heap에 저장되나요 아니면 일반적인 변수, 상수처럼 heap, stack에 저장되나요?

  • 이는 데이터의 type에 따라 결정이 된다.
  • Primitive type (null, undefined, boolean, number, string) 의 경우 stack에, Non-Primitive type (array, object) 의 경우 heap에 저장된다.

 

Hoisting

Hoisting의 예시와 개념

  • 그렇다면, 이건 결과가 어떻게 나올까? 마지막줄의 console.log() 는 출력이 될까?
    • 답은 그렇다. 출력 결과는 아래와 같다.
    j 0
    j 1
    j 2
    j 3
    j 4
    j 5
    j 6
    j 7
    j 8
    j 9
    after loop j is  10
  • for(var j=0; j<10; j++) { console.log('j', j); } console.log('after loop j is ', j);
  • after loop j is 10 이 출력이 될 수 있는 이유는, 컴파일 과정 중에 var j 를 맨 윗부분에서 선언해주기 때문이다. 즉 위의 코드는 아래와 같은 내용인 것이다
    • var 가 function scope 를 갖는다면서 왜 for문 안이 아니라 전역에 선언이 된 것일까? 왜냐하면 for문은 함수가 아니기 때문이다.
    var j;
    
    for (j = 0; j < 10; j++) {
      console.log("j", j);
    }
    console.log("after loop j is ", j);
  • 이처럼 컴파일 과정 중에 선언문을 맨 위로 끌어올려주어, 해당 scope 내에서 선언과 사용의 순서에 상관 없이 사용할 수 있도록 하는 것Hoisting 이라 한다. 함수가 변수보다 우선시된다.
  • 1번과 2번 코드는 3번과 같다. 4번의 경우 ReferenceError 가 뜨지 않고 undefined 가 뜨는 이유는, 생각해보건대 var foo; 는 맨 위로 끌어올려졌지만 값 할당이 console.log() 이후에 일어났기 때문에 var foo가 선언만 되고 값이 할당이 되지 않아서 undefined가 나온 것 같다.
  • // 1. foo = "bar"; var foo; console.log(foo); // bar // 2. foo = "bar"; console.log(foo); // bar var foo; // 3. var foo; foo = "bar"; console.log(foo); // bar // 4. console.log(foo); // undefined foo = "bar"; var foo;

 

Hoisting이 일어나는 경우와 그렇지 않은 경우

  • var 변수/함수의 선언만 위로 끌어올려진다.
  • const, let 같이 선언과 동시에 할당이 되는 경우 에는 끌어올려지지 않는다.
  Hoisting 여부
var O
let X
const X
  • 사실 Hoisting이 일어나지 않는 것은 아니다. 내부적으로는 모두 Hoisting이 일어나지만 TDZ로 인해 (실행 결과만 봤을 때에는 -let, const, 함수 선언식은- ReferenceError가 뜬다) hoisting이 일어나지 않는 것 처럼 보인다.
  • // 출력을 선언보다 먼저했다 // let & const console.log(a); // ReferenceError: a is not defined console.log(b); // ReferenceError: b is not defined let a = "a"; const b = "b"; // 함수 선언식 console.log(ff("15")); // ReferenceError: ff is not defined const ff = function(v) { // 함수 표현식 return v; };

 

var보다 let, const 사용을 지향하는 이유는 Hoisting과 관련이 있다고 생각한다

  • let과 const가 등장한 이래로 var보다는 let, const 사용을 지향해오고 있다. 그 이유는, 생각해보건대 hoisting 때문인 것 같다. var는 (for문의 예시 처럼) 종종 예측하기 어려운 상황을 만들 수 있다.

 

함수

함수 선언문

  • 구성 요소
    • 함수 이름
    • 매개변수
    • 함수 몸체
    • (optional: 반환 값)
function square(number) {
    return number*number;
}

 

함수 표현식

  • 함수의 선언과 동시에 변수에 할당한다.
  • var square = function(number) { return number*number; };
  • 함수가 선언될 때, 함수에 이름이 있냐 없냐에 따라 기명/익명 함수로 나뉜다.
    • 기명 함수
    • var foo = function multiply(a, b) { return a*b; };
    • 익명 함수
    • var bar = function(a, b) { return a * b; };
  • 함수 표현식이 뭔지는 알겠는데, 굳이 왜 하는걸까? 변수 안에 함수를 넣어야 하는 이유는 무엇일까?
    • 유성이가 또 좋은 질문을 해주었다. 그동안 그저 변수에 함수를 넣기만 했지, 왜 그렇게 썼는지 고민을 해 본 적이 없었다.
    • 이유는 함수를 변수처럼 사용하기 위해서 이다. 이를테면,
    1. 다른 함수의 인자로써 사용하거나
    2. 리턴 값으로 사용하거나
    3. 변수나 객체에 할당하거나
    4. 동적으로 객체의 프로퍼티를 생성 또는 할당하거나
    • 를 하기 위해서 이다.

 

참고 자료