변수명: 메모리 공간에 저장된 값을 식별할 수 있는 고유한 이름
변수 값: 변수에 저장된 값
할당: 변수에 값을 저장하는 것
참조: 변수에 저장된 값을 읽어들이는 것
컴퓨터는 CPU를 사용해 연산하고 메모리를 사용해 데이터를 기억한다.
메모리는 데이터를 저장할 수 있는 메모리 셀의 집합체로, 각 셀은 메모리 공간의 위치를 나타내는 고유의 메모리 주소를 갖는다.
10 + 20
만약 위에 같은 연산이 있다고 하자,
연산이 성공적으로 끝나면 연산 결과는 메모리에 저장된다.
그렇다면 연산 결과를 재사용하기 위해서는 어떻게 해야할까?
메모리 주소를 통해 연산 결과가 저장된 메모리 공간에 직접 접근해야한다.
그러나 메모리 주소를 통한 직접 접근은 권장되지 않는다.
1) 직접적 메모리 제어가 시스템에 치명적 오류를 불러올 수 있고,
2) 메모리 주소는 임의로 결정되므로 값이 저장될때마다
메모리 주소가 변경되기 때문이다.
따라서, 메모리 공간에 저장된 값을 다시 읽어들여 재사용할 수 있도록 저장된 메모리 공간에 상징적인 이름을 붙인 것이 바로 변수이다.
변수는 하나의 값을 저장하기 위한 수단이며, 배열이나 객체와 같은 자료구조를 사용하면 관련이 있는 여러개의 값을 그룹화하여 하나의 값처럼 사용할 수 있다.
식별자는 메모리 공간에 저장되어있는 어떤 값을 식별하고,
값에 접근하기위해 값이 저장되어있는 메모리 주소를 기억한다.
메모리 상에 존재하는 어떠한 값을 식별할 수 있는 이름은 모두 식별자라고 부르는데 변수, 함수, 클래스 등의 이름은 모두 식별자이다.
식별자는 1) 네이밍 규칙을 준수해야하며 2) 선언 (declaration)에 의해 JavaScript 엔진에 존재를 알린다.
변수 선언이란 변수를 생성하는 것,
즉 값을 저장하기 위한 메모리 공간을 확보하고 변수 이름과 확보된 메모리 공간의 주소를 연결하여 값을 저장할 수 있게 준비하는 것이다.
변수를 사용하려면 반드시 선언이 필요한데,
var, let, const 키워드를 사용하여 변수를 선언한다.
만약 선언하지 않은 식별자에 접근하면,
식별자를 통해 값을 참조하려했지만 자바스크립트 엔진이 등록된 식별자를 찾을 수 없을 때 발생하는 에러인 ReferenceError(참조에러)가 발생한다.
키워드
JavaScript 엔진이 수행할 동작을 규정한 일종의 명령어
1) 선언 단계
: 변수 이름을 등록하여 JavaScript 엔진에 변수의 존재를 알린다.
2) 초기화 단계
: 값을 저장하기 위한 메모리 공간을 확보하고 암묵적으로
undefined를 할당해 초기화 한다.
변수를 선언한 이후 변수에 값을 할당하지 않아도 확보된 메모리 공간에는 JavaScript 엔진에 의해 undefined라는 값이 암묵적으로 할당되어 초기화된다. (JS의 독특한 특징)
초기화
변수가 선언된 이후 최초로 값을 할당하는 것.
초기화 단계를 거치지 않으면 확보된 메모리 공간에
이전에 다른 애플리케이션이 사용했던 값이 남아있을 수 있는데, 이를 쓰레기 값이라고 한다.
var키워드는 암묵적으로 초기화를 수행하므로 이러한 위험으로부터 안전하다.
console.log(score); // undefined
var scope; // 변수 선언문
JavaScript는 소스코드를 한 줄씩 순차적으로 실행한다.
따라서 위의 예제와 같이 변수의 실행이 변수 선언보다 앞서있는 경우에는 ReferenceError(참조에러)가 발생할 것 처럼 보인다.
하지만 ReferenceError가 발생하지 않고 undefined가 출력되었는데, 그 이유는 변수 호이스팅 때문이다.
변수 호이스팅은 JavaScript 엔진이 변수 선언의 위치와 상관없이 선언을 최상단에 위치한것 처럼 동작하게하는 JavaScript 고유의 특징이다.
변수 선언뿐 아니라 변수, 함수, 클래스 등의 모든 식별자는 호이스팅된다.
변수에 값을 할당할 때는 할당 연산자 =
를 사용한다.
할당 연산자는 우변의 값을 좌변에 할당한다.
var score; // 변수 선언
score = 80; // 값의 할당
변수 선언과 값의 할당은 하나의 문으로 표현할 수도 있다
var score = 80; // 변수 선언 및 값의 할당
JavaScript 엔진은 변수 선언과 값의 할당을 하나의 문으로 단축 표현해도 2개의 문으로 나누어 각각 실행하는데,
변수 선언은 소스코드가 순차적으로 실행되는 시점인 런타임 이전에 먼저 실행되지만 값의 할당은 런타임에 실행되므로
둘의 실행시점이 다르다는 것에 주의해야한다.
console.log(score); // 변수선언 완료, undefined로 초기화
var score = 80;
console.log(score); // 80, 재할당됨
변수에 값을 할당할때는 이전값이 저장되어있는 메모리 공간을 지우고 재할당하는 것이 아니라 새로운 메모리 공간을 확보하고 그곳에 할당한다는 점을 주의해야한다
재할당이란 이미 값이 할당되어 있는 변수에 현재 저장되어있는 값을 버리고 새로운 값을 다시 할당하는 것이다.
var 키워드로 선언된 변수의 경우, 선언과 동시에 undefined으로 초기화되므로 엄밀히 따지자면 변수에 처음 값을 할당하는 것도 재할당이다.
만약 값을 재할당할 수 없어서 변수에 저장된 값을 변경할 수 없다면 상수(constant)라고 한다 ex)const
var score = 80; // 변수 선언 및 할당
score = 90; // 재할당
앞서 변수에 값을 재할당하면 이전 값이 저장되어있던 메모리 공간이 아닌 새로운 메모리공간을 확보한 후 새로운 값을 할당한다고 했었다.
따라서 위의 예제에서 score변수의 이전값인 undefined과 80은 현재 어떤 변수도 값으로 가지고 있지않다.
(어떤 식별자도 참조하지않는 메모리 공간)
아무도 사용하고 있지않은 이러한 불필요한 값들은
가비지 콜렉터에 의해 메모리에서 자동해제 된다.
단, 메모리 해제 시점은 예측할 수 없다.
*가비지 콜렉터
: 애플리케이션이 할당한 메모리 공간을 주기적으로
검사하여 더 이상 사용되지않는 메모리를 해제하는 기능
JavaScript는 가비지 콜렉터를 내장하고 있는
매니지드 언어로, 가비지 콜렉터를 통해 메모리 누수(leak)를 방지한다.
메모리 누수
프로그램이 필요하지않은 메모리를 계속 점유하고 있는 상태
프로그래밍 언어는 메모리 관리 방식에 따라 언매니지드 언어와 매니지드 언어로 분류된다.
*언매니지드 언어 ex) C언어
: 메모리 제어를 개발자가 주도할 수 있음
개발자의 역량에 따라 최적의 성능을 확보할 수 있지만,
반대의 경우 치명적 오류를 생산할 수도 있음
*매니지드 언어 ex) JS
: 메모리의 할당 및 해제를 위한 메모리 관리 기능을 언어 차원에서 담당하고 개발자의 메모리 제어를 허용하지않음
일정한 생산성을 확보할 수 있으나 성능 면에서
어느정도의 손실을 감수할 수 밖에 없다.
1) 식별자는 특수문자를 제외한 문자, 숫자, 언더스코어(_
),
달러 기호($
)를 포함할 수 있다.
2) 식별자는 특수문자를 제외한 문자, 숫자, 언더스코어(_
),
달러 기호($
)로 시작해야한다. 숫자는 허용하지않는다.
3) 예약어는 사용할 수 없다.
var firstname;
var firstName;
var FIRSTNAME;
JavaScript는 대소문자를 구별하므로, 위의 각 변수는 서로 다른 변수이다.
var 키워드는 ES5까지 변수를 선언할 수 있는 유일한 방법이었으나, 최근에는 사용하지않는 것을 권장한다.
var 키워드로 선언한 변수는 중복 선언이 가능한데,
이는 변수의 값을 의도치않게 변경하는 부작용을 가져올 수 있다.
var x =1;
var y = 1;
var x = 100;
var y;
console.log(x); // 100
console.log(y); // 1
var 키워드로 선언된 변수는 중복 선언시 초기화문 유무에 따라 다르게 동작한다.
초기화문
변수 선언과 동시에 초기값을 할당하는 문
초기화문이 있는 변수 선언문은 JavaScript 엔진에 의해 var키워드가 없는 것 처럼 동작하고, 초기화문이 없는 선언문은 무시된다.
var 키워드로 선언한 변수는 오직 함수의 코드 블록만을 지역변수로 인정하며, 함수 외부에 선언한 변수는 모두 전역 변수가 된다.
var x = 1;
if(true) {
var x =10;
}
console.log(x); / 10
위의 예제에서 if문 안의 x는 전역 변수이다.
if문 바깥에 이미 선언된 전역변수 x가 있으므로,
변수 x는 중복선언되어 변수값이 변경된다.
이는 의도치 않게 변수값을 변경하는 부작용을 야기하기도 한다.
var 키워드 변수는 변수 호이스팅에 의해 변수 선언문이 최상단에 끌어올려진 것 처럼 동작하는데, 이는 가독성을 떨어뜨리고 오류를 발생시킬 여지를 남긴다.
console.log(foo); // undefined
foo = 123;
console.log(foo); // 123
var foo;
let 키워드로 선언된 변수는 같은 스코프 내에서 중복 선언을 허용하지않는다.
let bar = 123;
ler bar = 456; // SyntaxErrror: bar is not defined
let 키워드로 선언한 변수는 모든 코드 블록(함수, if문, for문, while문, try/catch문)을 지역 스코프로 인정하는 블록 레벨 스코프를 따른다.
let foo = 1; // 전역변수
{
let foo = 2;
let bar = 3; // 지역변수
}
console.log(foo); // 1
console.log(bar); // ReferenceError: bar is not defined
JavaScript는 모든 선언을 호이스팅한다.
그러나 let 키워드로 선언한 변수는 변수 호이스팅이 발생하지 않는것 처럼 동작한다.
let 키워드로 선언한 변수는 '선언 단계'와 '초기화 단계'가 분리되어서 실행되는데, 런타임 이전에 자바스크립트 엔진에 의해 선언 단계가 암묵적으로 먼저 실행되지만 초기화 단계는 변수 선언문에 도달했을 때 실행된다.
만약 초기화 단계가 실행되기 이전에 변수에 접근하려고 하면 참조에러가 발생한다.
// 변수는 선언되었으나 아직 변수가 초기화되지않은
// 일시적 사각지대이므로 변수를 참조할 수 없음
console.log(foo); // 참조에러
let foo; // 변수 초기화
console.log(foo); // undefined
foo = 1; // 변수 할당
console.log(foo); // 1
스코프의 시작 지점부터 초기화 시작 지점까지 변수를 참조할 수 없는 구간을 일시적 사각지대라고 부른다.
let 키워드로 선언된 변수는 '호이스팅이 발생하지 않는 것 처럼 동작'할 뿐 실제로 호이스팅이 발생하지않는 것은 아니다.
let foo = 5;
{
console.log(foo); // ReferenceError: Cannot access 'foo' before initialization
let foo = 6;
}
만약 변수 호이스팅이 발생하지 않는다면 console.log(foo)는 전역변수 foo의 값을 출력해야한다.
그러나 지역변수 foo의 선언이 호이스팅되었으므로
'참조에러:초기화 이전에 foo에 접근할 수 없습니다.' 가 발생한다. (일시적 사각지대)
✨ 참고
https://velog.io/@modolee/javascript-let-const-hoisting
var 키워드로 선언한 전역 변수, 전역 함수, 암묵적 전역은 전역 객체 window의 프로퍼티로, 전역 변수처럼 사용할 수 있다.
(전역 객체의 프로퍼티를 참조할 때 window 생략가능)
// 전역 변수
var x = 1;
// 암묵적 전역
y = 2;
// 전역 함수
function foo() {}
console.log(window.x) // 1
console.log(x) // 1
암묵적 전역
선언하지 않은 변수에 값을 할당한 것
선언하지 않은 식별자에 값을 할당하면 전역객체의 프로퍼티가 되는데, 이는 변수가 아니므로 호이스팅이 발생하지는 않는다.
✨ 참고 - 암묵적 전역
https://velog.io/@nninnnin7/%EC%95%94%EB%AC%B5%EC%A0%81-%EC%A0%84%EC%97%AD
let 키워드로 선언한 전역 변수는 전역 객체의 프로퍼티가 아니므로 window.foo와 같이 접근할 수 없다.
let 전역 변수는 보이지 않는 개념적인 블록내에 존재하게 된다.
let x = 1;
console.log(window.x) // undefined
console.log(x) // 1
const 키워드는 주로 상수를 선언하기 위해서 사용한다.
특징은 대부분 let 키워드와 동일하다.
const 키워드로 선언한 변수는 반드시 선언과 동시에 초기화 해야한다.
const foo = 1;
const foo; // Uncaught SyntaxError: Missing initializer in const declaration
15.3.2 재할당 금지
var/let키워드로 선언한 변수가 재할당이 자유로운 것과 다리 const 키워드로 선언한 변수는 재할당이 금지되어있다.
const one = 1;
one = 2; // TypeError: Assignment to constant variable.
const 키워드는 상수를 표현하는데 사용된다.
상수는 재할당이 금지된 변수를 말하는데, 상태유지와 유지보수의 편의를 위해 상수 사용이 권장된다.
// 세율을 의미하는 0.1이 상수로 사용됨
const TAX_RATE = 0.1;
// 세전 가격
let preTaxPrice = 100;
// 세후 가격
let afterTaxPrice = preTaxPrice + (preTaxPrice * TAX_RATE);
console.log(afterTaxPrice); // 110
상수의 이름은 일반적으로 대문자로 선언되며, 언더스코어(_
)로 구분된 스네이크 케이스를 사용하여 상수임을 명확히 나타낸다.
const 키워드는 재할당을 금지하고 있다.
그러나 그것이 불변을 의미하는 것은 아니다.
const 키워드로 선언된 변수에 원시값을 할당했을 경우에는 값을 변경할 수 없지만, 객체를 할당한 경우에는 값을 변경할 수 있다.
const person = {
name: 'Lee';
};
person.name = 'park';
console.log(person); // {name: 'park'}
변수를 선언할 때는 기본적으로 const를 사용하고 let은 재할당이 필요한 경우에만 한정적으로 사용하는 것이 좋다.
재할당을 금지하는 const 키워드가 let에 비해 안전하기 때문이다.
일단 변수를 선언할 때는 const를 사용하고 반드시 재할당이 필요하다면 let으로 변경하되 이때 변수의 스코프는 최대한 좁게 만드는 것이 좋다.