글 작성자: 택시 운전사
반응형

Dynamic Typed Language

C언어와 JAVA와 같은 Static Typed Language에서는 변수를 선언할 때, intchar같은 자료형과 함께 선언합니다. 하지만 Python이나 JavaScript는 Dynamic Typed Language로 선언시 타입을 명시하지 않습니다. 파이썬의 경우에는 변수명만 선언하는 매우 간단한 식으로 만들어졌지만, JavaScript는 변수명 앞에 변수임을 알려주는 var를 붙여서 표현했습니다.

변수의 범위 (Variable Scope)

변수는 선언과 함께 해당 변수를 참조할 수 있는 공간적 영역을 가지게 됩니다. JavaScript에서 변수는 함수 범위(function-scoped)와 전역 범위(global-scoped), 블록 범위(block-scoped) 중 하나를 갖게 됩니다.

var (function-scoped)

varfunctional-scoped로 변수를 정의합니다.

// var가 변수를 function-scoped로 정의하기 때문에 for문이 끝난 후 i의 출력이 정상적으로 되는 것을 볼 수 있습니다.
// 이건 var가 hoisting이 되었기 때문입니다.
for(var j=0; j<10; j++) {
  console.log('j', j)
}
console.log('after loop j is ', j) // after loop j is 10

// 아래의 경우에는 에러가 발생합니다.
function counter () {
  for(var i=0; i<10; i++) {
    console.log('i', i)
  }
}
counter()
console.log('after loop i is', i) // ReferenceError: i is not defined

IIFE(immediately-invoked function expression)으로도 function-scoped를 확인할 수 있습니다.

(function() {
  // var로 선언한 변수 i는 여기까지만 hoisting됩니다.
  for(var i=0; i<10; i++) {
    console.log('i', i)
  }
})()
console.log('after loop i is', i) // ReferenceError: i is not defined

하지만 여기서 var를 명시해주지 않는다면 global variable로 인식해 hoisting 합니다.

// hoisting으로 var i가 global variable로 선언된 것처럼 보임
(function() {
  for(i=0; i<10; i++) {
    console.log('i', i)
  }
})()
console.log('after loop i is', i) // after loop i is 10

IIFE는 자체로 쓰이는 함수로 그 안에서 선언한 변수가 global variable이 된다는 것은 적절치 않습니다.
이를 막기 위해 `use strict'를 사용할 수 있습니다.

// iife_use_strict_var.js
(function() {
  'use strict'
  for(i=0; i<10; i++) {
    console.log('i', i)
  }
})()
console.log('after loop i is', i) // ReferenceError: i is not defined

하지만 코드가 더 복잡해진 느낌입니다.
문제는 이것 말고도 더 있습니다.

// problem_var.js
// 이미 만들어진 변수이름으로 재선언했는데 아무런 문제가 발생하지 않는다.
var a = 'test'
var a = 'test2' // 이미 만들어진 변수 이름을 재선언해도 문제가 일어나지 않습니다.

c = 'test' // hoisting으로 인해 정의되지 않았을 것 같은 변수에 값을 대입해도 ReferenceError가 나지 않습니다.
var c

그리고 이러한 문제들을 해결하기 위해 constlet가 등장했습니다.

const, let (block-scoped)

constletvarfunction-scoped를 해결하기 위한 방법으로 block-scoped를 가진 변수를 선언하기 위해 ES2015(ES6)에서 추가된 문법입니다. constlet은 둘 다 변수의 재선언이 불가능합니다.
둘의 차이점은 constimmutable 즉, 값의 변경이 불가능하지만, letmutable 값의 변경이 가능합니다.

// let.js
let a = 'test'
let a = 'test2' // Uncaught SyntaxError: Identifier 'a' has already been declared
a = 'test3'     // 변수 값의 변경이 가능합니다.

// const.js
const b = 'test'
const b = 'test2' // Uncaught SyntaxError: Identifier 'a' has already been declared
b = 'test3'    // Uncaught TypeError:Assignment to constant variable.

추가적으로 이야기하자면 let, consthoisting이 일어납니다.
다만 varfunction-scopedhoisting된 반면, constletblock-scoped단위로 hoisting이 됩니다.
또한, constletTDZ(Temporal Dead Zone)에 의해 제약을 받아 변수가 초기화되기 전에 접근하려 하면, var처럼 undefined를 반환하지 않고, ReferenceError를 발생시켜서, 코드를 예측 가능하고 잠재적인 버그를 쉽게 찾아낼 수 있도록 합니다.

// hoisting_let.js
c = 'test' // ReferenceError: c is not defined
let c

이러한 특징때문에 letconst는 겉으로 보기에 hoisting이 일어나지 않는 것처럼 보입니다.

참고

반응형