04장 변수

4.1 변수란 무엇인가? 왜 필요한가?

변수(variable)는 하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 그 메모리 공간을 식별하기 위해 붙인 이름을 말한다.

다음과 같은 자바스크립트 코드를 살펴보자

10 + 20;

자바스크립트 엔진은 위 코드를 평가(evaluation)하기 위해 10, 20, + 라는 기호(리터럴literal과 연산자operator)의 의미를 알고 있어야 하며, 10 + 20이라는 표현식(expression)의 의미도 해석(파싱parsing)할 수 있어야 한다.

자바스크립트 엔진이 + 연산을 수행하기 위해 먼저 피연산자(operand) 10, 20을 기억해야 한다. 컴퓨터는 이를 메모리에 저장하고 읽는다. CPU는 메모리에 있는 피연산자를 읽어 + 연산을 수행한 뒤 30이라는 값을 다른 메모리 공간에 저장한다.

하지만 이대로는 30이라는 메모리 주소에 직접 접근하는 것 말고는 값을 재사용할 수 없다. 자바스크립트는 개발자가 메모리에 직접 접근하도록 허용하지 않으므로 30이라는 값에 접근할 수 없다.

이를 해결하기 위해 변수라는 개념이 존재한다.

var result = 10 + 20;

10 + 20을 통한 연산의 결과를 result라는 메모리 공간에 붙여진 이름을 통해 저장하여 재사용할 수 있게 됐다.

변수에 값을 저장하는 것을 할당이라 하고, 변수에 저장된 값을 읽어 들이는 것을 참조라 한다.

4.2 식별자

식별자(identifier)는 어떤 값을 구별해서 식별할 수 있는 고유한 이름을 말한다.

값은 메모리 공간에 저장되어 있다. 따라서 식별자는 메모리 공간에 저장되어 있는 어떤 값을 구별해서 식별해낼 수 있어야 한다. 이를 위해 식별자는 어떤 값이 저장되어 있는 메모리 주소를 기억해야 한다.

즉 식별자는 값이 아니라 메모리 주소를 기억하고 있다.

변수, 함수, 클래스 등의 이름은 모두 식별자이다. 즉 메모리 상에 존재하는 어떤 값을 식별할 수 있는 이름은 모두 식별자라고 부른다.

변수, 함수, 클래스 등의 이름과 같은 식별자는 네이밍 규칙을 주수해야 하며, 선언에 의해 자바스크립트 엔진에 식별자의 존재를 알린다.

4.3 변수 선언

변수 선언(declaration)이란 값을 저장하기 위한 메모리 공간을 확보(allocate)하고 변수 이름과 확보된 메모리 공간의 주소를 연결(name binding)해서 값을 저장할 수 있게 준비하는 것이다.

변수를 사용하려면 반드시 선언이 필요하다. 변수를 선언할 떄는 var, let, const 키워드를 사용한다.

let, const 키워드는 ES6에서 도입되었다.

키워드는 자바스크립트 코드를 해석하고 실행하는 자바스크립트 엔진이 수행할 동작을 규정한 일종의 명령어이다.

ES5 vs ES6 var 키워드는 여러 단점이 있다. block-level-scope를 지원하지 않고 function-level-scope를 지원한다는 것이다. ES6는 ES5의 superset이며 ES5 환경에서도 트랜스파일러를 이용해 ES6를 컴파일 할 수 있다. (babel과 polyfill 참고)

var score;

변수 선언에 의해 확보된 메모리 공간은 비어 있을 것으로 생각할 수 있으나 확보된 메모리 공간에는 자바스크립트 엔진에 의해 undefined라는 값이 암묵적으로 할당되어 초기화된다.(이것은 자바스크립트의 독특한 특징이다.)

초기화(initialization)는변수가 선언된 이후 최초로 값을 할당하는 것을 말한다.

자바스크립트 엔진은 변수 선언을 다음과 같은 2단계에 거쳐 수행한다.

  • 선언 단계 - 변수 이름을 등록해서 자바스크립트 엔진에 변수의 존재를 알린다.

  • 초기화 단계 - 값을 저장하기 위한 메모리 공간을 확보하고 암묵적으로 undefined를 할당해 초기화한다.

초기화 단계를 거치지 않으면 확보된 메모리 공간에는 이전에 다른 애플리케이션이 사용했던 값이 남아있을 수 있다. 이러한 값을 쓰레기 값이라 한다. 하지만 자바스크립트는 암묵적으로 초기화를 수행하므로 이런 위험이 없다.

선언하지 않은 식별자에 접근하면 ReferenceError(참조에러)가 발생한다.

자바스크립트는 컴파일 언어이다. Javascript는 실행되기 전에 마이크로초(또는 그 이하) 시간동안 컴파일 된다. (JIT, with lazy compile and even hot re-compile 등 기술 참고) Tokenizing/Lexing → Parsing → Code-Generation

var a = 2;를 만나면

  1. 컴파일러는 var 키워드를 보고 Scope에 해당 변수 선언을 요청한다. 그 후 엔진이 실행할 수 있는 코드를 생성한다 a = 2

  2. 프로그램 실행 후 a = 2를 만난 엔진은 Scope에서 해당 a를 찾고 값을 할당하는 일을 한다. (LHS look-up)

4.4 변수 선언의 실행 시점과 변수 호이스팅

console.log(score); //undefined

var score;

위 코드에서 참조에러가 발생하지 않고 undefined가 출력이 되는 이유는 변수 선언이 소스코드가 한 줄씩 순차적으로 실행되는 시점, 즉 런타임이 아니라 그 이전 단계에서 먼저 실행되기 때문이다.

자바스크립트 엔진은 소스코드를 한 줄씩 순차적으로 실행하기에 앞서 먼저 소스코드의 평가 과정을 거치면서 소스코드를 실행하기 위한 준비를 한다.

이때, 소스코드 실행을 위한 준비 단계인 소스코드의 평가 과정에서 자바스크립트 엔진은 변수 선언을 포함한 모든 선언문(변수 선언문, 함수 선언문등)을 소스코드에서 찾아내 먼저 실행한다.

그리고, 소스코드의 평가 과정이 끝나면 비로소 변수 선언을 포함한 모든 선언문을 제외하고 소스코드를 한 줄식 순차적으로 실행한다.

즉, 자바스크립트 엔진은 변수 선언이 어디에 있든 상관없이 다른 코드보다 먼저 실행된다.

이처럼 변수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트 고유의 특징을 변수 호이스팅이라 한다.

4.5 값의 할당

변수에 값을 할당할 때는 할당(assignment) 연산자 =를 사용한다. 할당 연산자는 우변의 값을 좌변의 변수에 할당한다.

변수 선언은 소스코드가 순차적으로 실행되는 시점인 런타임 이전에 먼저 실행되지만 값의 할당은 소스코드가 순차적으로 실행되는 시점인 런타임에 실행된다.

console.log(score); // undefined

var score;
score = 80;

console.log(score); // 80

변수에 값을 할당할 때는 이전 값 undefined가 저장되어 있는 메모리 공간을 지우고 그 메모리 공간에 할당 값 80을 새롭게 저장하는 것이 아니라 새로운 메모리 공간을 확보하고 그곳에 할당 값 80을 저장한다.

4.6 값의 재할당

재할당이란 이미 값이 할당되어 있는 변수에 새로운 값을 또다시 할당하는 것을 말한다.

var 키워드로 선언한 변수는 선언과 동시에 undefined로 초기화되기 때문에 엄밀히 말하자면 변수에 처음으로 값을 할당하는 것도 사실은 재할당이다.

값을 재할당할 수 없어서 변수에 저장된 값을 변경할 수 없다면 변수가 아니라 상수다.

상수는 단 한 번만 할당할 수 있는 변수다.

변수에 값을 재할당할때는 처음에 변수에 값을 할당할 때처럼 이전 값이 저장되어 있던 메모리 공간에 새로운 값을 저장하는 것이 아니라 새로운 메모리 공간을 확보하고 그 메모리 공간에 새로운 값을 저장하는 것이다.

이후, 어떤 식별자와도 연결되어 있지 않은 불필요한 값들은 가비지 컬렉터에 의해 메모리에서 자동 해제된다. 단, 메모리에서 언제 해제될지는 예측할 수 없다.

자바스크립트에서 재할당을 할 때는 기존에 있던 메모리 공간에 값을 할당하지 않습니다. 만약 기존에 있던 메모리 공간의 크기가 할당할 값보다 작다면 그 메모리를 늘려주는 작업도 해야 할 겁니다. 비용이 많이 들죠 그렇지 않고 다른 메모리 공간에 값을 저장해놓고 식별자 foo는 그 메모리 공간을 가리키기만 하면 되겠죠 즉 메모리의 효율적인 처리를 위해 이런 동작을 하고 재할당이 일어날 때 마다 새로운 메모리 공간을 할당하며 기존에 있던 메모리 공간은 가비지 컬렝터의 대상이 됩니다

4.7 식별자 네이밍 규칙

식별자는 다음과 같은 네이밍 규칙을 준수해야한다.

  • 식별자는 특수문자를 제외한 문자, 숫자, 언더스코어(_), 달러 기호($)를 포함할 수 있다.

  • 단, 식별자는 특수문자를 제외한 문자, 언더스코어(_), 달러 기호($)로 시작해야한다. 숫자롤 시작하는 것은 허용하지 않는다.

  • 예약어는 식별자로 사용할 수 없다.

    • 예약어는 프로그래밍 언어에서 사용되고 있거나 사용될 예정인 단어를 말한다. (ex. await, continue, if, break등등)

변수는 쉼표로 구분해 하나의 문에서 여러 개한번에 선언할 수 있다. 단, 가독성이 나빠지므로 권장하지는 않는다.

var person, $elem, _name, first_name;

ES5부터 유니코드 문자를 허용하므로 알파벳 외의 한글이나 일본어 식별자도 사용할 수 있다.

다음 식별자는 명명 규칙에 위배되므로 변수 이름으로 사용할 수 없다.

**var first-name; // SyntaxError: Unexpected token
var 1st; // SyntaxError: Invalid or unexpected token
var this; // SyntaxError: Unexpected token this**

Last updated