Variable
변수변수(Variable) : 값의 위치를 가리키는 상징적인 이름
( 하나의 값을 저장하기 위해 할당된 메모리 공간, 그 공간을 식별하기 위한 이름 )
변수 이름(변수명) : 저장된 값을 식별 할 수 있는 이름
변수 값 : 변수에 저장 된 값
할당(assignment) : 변수에 값을 저장, 기억시키는 것
참조(reference) : 변수에서 저장된 값을 불러오는 것
식별자(identifier)
는 어떤 값을 구별해서 식별할 수 있는 고유한 이름을 의미한다. 즉, 변수, 함수, 클래스 등 모든 선언에 의한 존재들의 '이름'은식별자(identifier)
이다.
var hello; // undefined 출력 hello = 'hello!'; // 'hello!' 출력
변수를 사용하려면 반드시 선언이 필요하다. 이 때 var
, let
, const
키워드가 사용된다.
변수의 선언은 선언을 통해 자바스크립트에 변수의 존재를 알리고, 초기화를 통해 변수의 값을 저장할 공간을 확보한 뒤 undefined
를 할당한다.
만약 선언하지 않은 식별자를 호출 할 경우 ReferenceError(참조 에러)
가 발생한다.
이렇게 선언을 통한 초기화를 한 뒤 변수명에 값을 할당해 주면 된다.
그러나 위의 두줄로 나뉘어 진 선언문을 다음과 같이 한 줄로 표현할 수 있고 동일하게 작동한다.
var hello = 'hello!'; // 'hello!' 출력
console.log(hello); //undefined var hello;
아직 선언되지 않은 변수가 호출 되고, 그 다음에 변수가 선언되었다. 이 경우 ReferenceError
를 예상 할 수도 있지만 undefined
가 출력된다.
그 이유는 소스코드가 실행되는 시점(런타임) 이전에 소스코드 평가 과정에서 자바스크립트 엔진이 모든 선언문을 찾아 먼저 실행하기 때문이다.
하지만 이 때, 변수 선언과 값이 모두 먼저 일어나는 것이 아니라 선언만 먼저 이루어지고 아직 값은 할당되지 않은 상태이다. 즉 위에서 부터 아래로 한 줄씩 순차적으로 실행하는 것이 기본 순서이나 이 런타임 이전의 평가 과정에서 선언문만 먼저 찾아내 실행 한 뒤, 다시 위에서 부터 아래로 순차적으로 실행해 나가면서 값이 할당된다는 것이다.
이처럼 변수 선언문이 코드의 가장 위에 작성된 것 처럼 동작하는 자바스크립트의 특징을 변수 호이스팅
이라고 한다. 호이스팅
은 변수에만 이루어 지는 것이 아니라 모든 선언문이 해당한다.
var
ES5
까지는 유일한 변수 선언 방법이었다. 하지만 몇 가지 문제가 있어 이를 보완하기 위해 ES6
에서 다음에 설명할 let
과 const
가 도입되었다. 이후 let
과 const
의 사용이 권장되나 ES6
이전 코드는 var
키워드를 통해 구현되어 있을 것이므로 알아두는 것이 좋다.
var
로 선언한 변수는 중복 선언이 가능하다.
var hello = 'hello'; var hello = 'hello!!!';
var
키워드로 hello
가 중복 선언 되었다. 위와 같은 경우 재선언 되어 'hello!!!'
가 오류없이 출력되어 나온다. 이 때 만약 값의 재할당을 원한 것이 아니라 두가지의 다른 변수선언을 원했다면 변수 하나가 사라져 버리므로 문제가 된다.
function scoped
)var number = 10; if(true) { //함수의 내용 var number = 100; }
선언된 함수 하나의 안에서만 사용 될 수 있다. 함수 외부 에서는 접근이 불가하다.
그런데 이 때, 위의 예시처럼 함수 안의 변수가 선언되기 전에 전역 변수로 number
가 선언되었다. 전역 변수 number
가 가지고 있는 값은 10
이다. 그 아래에 if문
안에 동일한 이름의 number
변수가 선언되었고 할당된 값은 100
이다.
var
로 선언된 변수는 코드 블록 안에서 재선언 하는 경우 모두 전역변수가 된다. 즉 함수레벨 스코프는 전역 변수의 남발의 가능성을 높여 스코프오염의 위험성을 높인다.
위에서 설명한 호이스팅을 그대로 보여준다. 값 할당 전에 출력 키워드를 만나면 undefined
를 출력하고 이 후 값 할당 후 출력 키워드를 만나면 할당된 값을 출력한다.
위에서 본 var
에는 전역 변수가 남발될 가능성과, 재선언으로 인한 문제가 있었다. 이러한 문제들을 보완하기 위해 나온 키워드가 let
과 const
이다. 둘은 비슷하지만 차이가 있다.
const
/ let
const hello = 'hello'; const hello = 'hello!'; // SyntaxError: Identifier 'hello' has already been declared
const
와 let
으로 선언한 변수는 중복 선언 할 수 없다. 위의 var
에서 의도치 않게 같은 변수명을 재선언하게 되어 먼저 선언된 변수까지 값이 재할당 되는 부작용을 막기 위한 부분이다. 중복 선언이 될 경우 SyntaxError: Identifier 'hello' has already been declared
라는 에러가 나온다.
block-level scope
)const
와 let
은 모든 코드 블록을 각각의 지역 스코프로 분리한다. 이는 함수
, if문
, for문
등 {}
로 감싸지는 공간 모두 각각의 지역으로 분리한다는 것이다.
하나의 함수 안에서도 {}
로 분리된 지역이라면 같은 이름의 변수를 새롭게 생성할 수 있으며, 각 지역에서의 변수는 각각의 역할을 한다.
console.log(hello); //caught ReferenceError: hello is not defined let hello;
const
와 let
은 마치 호이스팅이 일어나지 않는 것처럼 동작한다. var
는 undefined
를 출력했던 것과 달리 const
와 let
은 referenceError
가 발생한다.
하지만 그렇다고 해서 호이스팅이 일어나지 않는다는 의미는 아니다. 호이스팅은 동일하게 일어나지만 '스코프의 시작 지점' 부터 '변수 선언문(초기화)' 단계까지 해당 변수를 참조할 수 없다. 이러한 참조 불가한 구간을 일시적 사각지대(Temporal Dead Zone)
이라고 한다.
const vs. let
const
키워드는 선언과 값의 할당이 동시에 이루어 져야 한다.
그렇지 않으면 SyntaxError: Missing initializer in const declaration
에러가 발생한다.
let
키워드는 선언과 값의 할당을 각각 분리하여 할 수 있다.
const
로 선언한 변수는 값을 재할당 할 수 없다. 그래서 상수
의 개념으로 쓰이기도 한다. 이는 상태 유지와 가독성, 유지보수 등을 위해 적극적으로 사용하면 좋다.
예를 들어 세율 같이 쉽게 바뀌지 않는 숫자이면서 코드 전체에 동일하게 적용되는 값 등은 매번 퍼센트를 작성하기 보다 const
로 선언 후 변수를 사용하면 이 후 값이 변경 되어 코드를 수정하게 되어도 변수 선언문을 찾아 한번만 값을 변경하면 되므로 유지보수에 매우 효율적이다.
그러나 let
은 재할당이 가능하다. 이때 var
와의 차이점은 스코프 영역에 있다.
- 식별자는 특수문자를 제외한 문자, 숫자, 언더스코어(_), 달러기호($)를 포함할 수 있다.
- 식별자는 문자, 언더스코어(_), 달러기호($) 로 시작해야 한다. 숫자로는 시작할 수 없다.
- 예약어는 식별자로 사용할 수 없다.
변수의 이름은 자유롭게 설정할 수 있으나 해당 변수의 목적이 무엇인지를 명확히 담을 수 있도록 해야한다. 이는 코드의 가독성을 높여 유지보수에 도움을 준다.
let sayHello; // 카멜 케이스(camelCase) let say_hello // 스네이크 케이스(snake_case) let SayHello; // 파스칼 케이스(PascalCase)
일관성을 유지하여 작성한다면, 어느 케이스를 사용해도 무방하다. 하지만 일반적으로 변수나 함수의 이름에는 카멜 케이스
를 사용하고 생성자 함수, 클래스의 이름에는 파스칼 케이스
가 사용되므로 이를 따르는 것이 가독성을 높이는 데 유리하다.
코드를 작성하다 보면 변수가 많아지고 이러한 변수들에게 매번 명확한 이름을 부여하기는 매우 어렵다. 다음은 변수명을 짓는데 도움을 주는 사이트이다.