네로개발일기

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

2. 변수

변수(Variable)는 프로그램에서 사용되는 데이터를 일정기간동안 기억하여 필요한 때에 다시 사용하기 위해 데이터에 고유의 이름인 식별자(identifier)를 명시한 것이다. 변수에 명시한 고유한 식별자를 변수명이라 하고 변수로 참조할 수 있는 데이터를 변수값이라 한다.

 

변수는 var, let, const 키워드를 사용하여 선언하고 할당 연산자를 사용해 값을 할당한다. 그리고 식별자인 변수명을 사용해 변수에 저장된 값을 참조한다.

 

1) 동적 타이핑(Dynamic Typing)

자바스크립트는 동적 타입(dynamic/weak type) 언어이다. 이것은 변수의 타입 지정(Type annotation)없이 값이 할당되는 과정에서 값의 타입에 의해 자동으로 타입이 결정(Type Inference)될 것이라는 뜻이다. 따라서 같은 변수에 여러 타입의 값을 할당할 수 있다. 이를 동적 타이핑(Dynamic Typing)이라 한다.

var foo;

console.log(typeof foo); //undefined

foo = null;
console.log(typeof foo); // object

foo = {};
console.log(typeof foo); // object

foo = 3;
console.log(typeof foo); // number

foo = 3.14;
console.log(typeof foo); // number

foo = 'Hi';
console.log(typeof foo); // string

foo = true;
console.log(typeof foo); // boolean

 

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

console.log(foo); // 1. undefined

var foo = 123;
console.log(foo); // 2. 123

{
	var foo = 456;
}
console.log(foo); // 3. 456

var 키워드를 사용하여 선언한 변수는 중복 선언이 가능하기 때문에 위 코드는 문법적으로 문제가 없다.

1에서 변수 foo는 아직 선언되지 않았으므로 ReferenceError: foo is not defined 가 발생할 것을 기대했겠지만 콘솔에는 undefined가 출력된다.

이것은 C-family 언어와는 차별되는 자바스크립트의 특징으로 모든 선언문은 호이스팅(Hoisting)되기 때문이다.

 

호이스팅이란 var 선언문이나 function 선언문 등 모든 선언문이 해당 scope의 선두로 옮겨진 것처럼 동작하는 특성을 말한다. 즉, 자바스크립트는 모든 선언문(var, let, const, function, function*, class)이 선언되기 이전에 참조 가능하다.

변수가 어떻게 생성되며 호이스팅은 어떻게 이루어지는지 좀 더 살펴보면 변수는 3단계에 걸쳐 생성된다. 

1) 선언 단계 (Declaration phase)

변수 객체(Variable Object)에 변수를 등록한다. 이 변수 객체는 스코프가 참조하는 대상이 된다.

2) 초기화 단계 (Initializing phase)

변수 객체에 등록된 변수를 메모리에 할당한다. 이 단계에서 변수는 undefined로 초기화된다.

3) 할당 단계(Assignment phase)

undefined로 초기화된 변수에 실제값을 할당한다.

 

var 키워드로 선언된 변수는 선언 단계와 초기화 단계가 한번에 이루어진다. 즉, 스코프에 변수가 등록되고 변수는 메모리에 공간을 확보한 후 undefined로 초기화된다. 따라서 변수 선언문 이전에 변수에 접근하여도 Variable Object에 변수가 존재하기 때문에 에러가 발생하지 않는다. 다만 undefined를 반환한다. 이러한 현상을 변수 호이스팅이라 한다.

 

이후 변수 할당문에 도달하면 비로소 값의 할당이 이루어진다.

var 키워드로 선언된 변수의 생명 주기

 

앞에서 살펴본 예제를 호이스팅 관점에서 다시 보면 1이 실행되기 이전에 var foo = 123; 이 호이스팅 되어 1 구문 앞에 var foo; 가 옮겨진다.  하지만 변수 선언 단계와 초기화 단계가 할당 단계와 분리되어 진행되기 때문에 이 단계에서는 foo에는 undefined가 할당되어 있다. 변수 foo에 값이 할당되는 것은 2행에서 실행된다.

 

자바 스크립트의 변수는 다른 C-family와는 달리 블록 레벨 스코프(block-level scope)를 가지지 않고 함수 레벨 스코프 (function-level scope)를 갖는다. 단, ES6에서 도입된 let, const 키워드를 사용하면 블록 레벨 스코프를 사용할 수 있다.

* 함수 레벨 스코프 (Function-level scope)

함수 내에서 선언된 변수는 함수 내에서만 유효하며 함수 외부에서는 참조할 수 없다. 즉, 함수 내부에서 선언한 변수는 지역 변수이며 함수 외부에서 선언한 변수는 모두 전역 변수이다.

* 블록 레벨 스코프 (Block-level scope)

코드 블록 내에서 선언된 변수는 코드 블록 내에서만 유효하며 코드 블록 외부에서는 참조할 수 없다.

 

따라서 코드 블록 내의 변수 foo는 전엳변수이므로 전역에 선언된 변수 foo에 할당된 값을 재할당하기 때문에 3의 결과는 456이 된다.

 

3) var 키워드로 선언된 변수의 문제점

ES5에서 변수를 선언할 수 있는 유일한 방법은 var 키워드를 사용하는 것이다. var 키워드로 선언된 변수는 아래와 같은 특징을 갖는다. 이는 다른 C-family 언어와는 차별되는 특징(설계상 오류)으로 주의를 기울이지 않으면 심각한 문제가 발생한다.

1. 함수 레벨 스코프 (Function-level scope)

- 전역 변수의 남발

- for loop 초기화 식에서 사용한 변수를 for loop 외부 또는 전역에서 참조할 수 있다.

2. var 키워드 생략 허용

- 의도하지 않은 변수의 전역화

3. 중복 선언 허용

- 의도하지 않은 변수값 변경

4. 변수 호이스팅

- 변수를 선언하기 전에 참조가 가능

 

대부분의 문제는 전역변수로 인해 발생한다. 전역변수는 간단한 애플리케이션인 경우, 사용이 편리한 면이 있지만 불가피한 상황을 제외하고 사용을 억제해야 한다. 전역 변수는 유효 범위(scope)가 넓어서 어디서 어떻게 사용될 지 파악하기 힘들다. 이는 의도하지 않은 변수의 변경이 발생할 수 있는 가능성이 증가한다. 또한 여러 함수와 상호 의존하는 등 부수 효과(side effect)가 있을 수 있어서 복잡성이 증가한다.

변수의 유효범위는 좁을 수록 좋다.

ES6는 이러한 var의 단점을 보완하기 위해 let과 const 키워드를 도입하였다.

 

 

 

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !