자바스크립트 ESC6 버전부터 변수를 선언할 때 var
예약어 외에 let
, const
예약어를 사용할 수 있다. const
는 상수값을 선언할 때 이용한다.
scope
: 변수를 선언하고 사용할 때 변수가 적용되는 범위
변수는 선언된 함수 안에서만 접근이 가능하다. 즉, 변수의 scope는 함수의 scope를 따른다.
Local Variable
: 한 함수 안에서만 사용할 수 있는 변수Global Variable
: 스크립트 소스 전체에서 사용할 수 있는 변수
지역변수는 전역변수보다 우선순위가 높기 때문에, 변수 이름이 같으면 지역변수가 더 우선된다.
let last_name = 'lee';
function func() {
let first_name = 'youjin';
let last_name = 'park';
return last_name + ' ' + first_name;
}
func(); // 'park youjin'
console.log(last_name); // 'lee'
여기서는 last_name이 전역과 func() 함수에 선언되었다. func() 함수 안에서 선언된 last_name이 지역변수이므로 더 우선순위가 높다. 그러나 지역변수는 해당 scope 안에서만 유효하며, 바깥 함수에서는 안쪽 함수/변수에 접근하지 못하므로 전역에서의 last_name은 'lee'이다.
var
는 Function Scope를 가진다. var로 선언된 변수의 scope는 곧 내가 속한 함수의 scope이다.
let
, const
는 Block Scope를 가진다. let이나 const로 선언된 변수의 scope는 내가 속한 블록 { }의 scope이다.
/* var 키워드를 사용했을 때 */
for (var i=0; i<5; i+=1) {
console.log(i); // 0 1 2 3 4
};
console.log(i); // 5
/* let 키워드를 사용했을 때 */
for (let j=0; j<5; j+=1) {
console.log(j); // 0 1 2 3 4
}
console.log(j); // ReferenceError
var를 사용한 변수는 함수의 scope를 따른다. 여기서는 전역이므로 스크립트 어디에서나 'i'에 접근할 수 있다. 따라서 for문이 끝나도 'i'에 접근할 수 있다.
let을 사용한 변수는 블록의 scope를 따른다. for문은 블록({ })으로 감싸져 있다. let으로 선언된 'j'는 for문의 블록에서만 유효하다. 따라서 for문 밖에서는 'j'에 접근할 수 없다. 바깥 함수에서는 내부 함수/변수에 접근할 수 없기 때문이다.
블록 내부에서 같은 이름으로 변수를 선언하면 변수가 외부 변수와 충돌하지 않고 외부 변수를 가린다. 내부 블록에서는 내부 블록에서 선언한 변수만 볼 수 있다. 이렇게 블록이 다른 경우 내부 변수가 외부 변수를 가리는 현상을 섀도잉이라고 한다.
// 전역 스코프
let pi = 3.14
console.log(`파이 값은 ${pi}입니다.`) // 파이 값은 3.14입니다.
// 블록을 사용한 스코프 생성
{
let pi = 3.141592
console.log(`파이 값은 ${pi}입니다.`) // 파이 값은 3.141592입니다.
}
console.log(`파이 값은 ${pi}입니다.`) // 파이 값은 3.14입니다.
// 함수 블록을 사용한 스코프 생성
function sample() {
let pi = 3.141592
console.log(`파이 값은 ${pi}입니다`)
}
sample() // 파이 값은 3.141592입니다.
console.log(`파이 값은 ${pi}입니다.`) // 파이 값은 3.14입니다.
호이스팅
은 인터프리터가 변수 / 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미한다.
쉽게 말해, 코드 실행 전에 변수선언 / 함수선언이 해당 스코프의 최상단으로 끌어 올려진 것 같은 현상을 말한다.
자바스크립트 엔진은 코드를 실행하기 전 실행 가능한 코드를 형상화하고 실행 컨텍스트를 위한 과정을 거친다. (실행 컨텍스트는 코드가 실행되기 위해 필요한 환경을 의미)
코드를 실행하기 전 실행 컨텍스트를 위한 과정에서 모든 선언 (var
, let
, const
, function
, class
)을 스코프에 등록한다. 코드 실행 전 이미 변수선언 / 함수선언이 저장되어 있기 때문에 선언문보다 참조 / 호출이 먼저 나와도 오류 없이 동작한다. 정확히는 var 키워드로 선언한 변수와 함수 선언문일 경우 오류 없이 동작한다.
자바스크립트의 모든 선언에는 호이스팅이 일어난다. 그런데 let, const, class를 이용한 선언문은 호이스팅이 발생하지 않는 것처럼 동작한다. var 키워드로 선언된 변수와는 달리 let 키워드로 선언된 변수를 선언문 이전에 참조하면 참조 에러(ReferenceError)가 발생한다. var 키워드는 선언과 함께 undefined로 초기화되어 메모리에 저장되는데, let과 const는 초기화되지 않은 상태로 선언만 메모리에 저장되기 때문이다. 초기화 되지 않으면 변수를 참조할 수 없다. 그래서 참조 에러를 일으키는 것이다.
console.log(my_name); // reference error
let my_name = "nabang"
console.log(my_name); // undefined
var my_name = "nabang"
변수의 생성
1. 선언 단계 (Declaration phase) : 변수를 실행 컨텍스트의 변수 객체에 등록한다.
2. 초기화 단계 (Initialization phase) : 변수 객체에 등록된 변수를 위한 공간을 메모리에 확보한다.이 단계에서 변수는 undefined로 초기화 된다.
3. 할당 단계 (Assignment phase) : undefined로 초기화된 변수에 실제 값을 할당한다
var
로 선언한 변수는 선언 단계와 초기화 단계가 한번에 이뤄진다. 즉, 스코프에 변수를 등록(선언 단계)하고 메모리에 변수를 위한 공간을 확보한 후, undefined로 초기화한다. 따라서 변수 선언문 이전에 변수에 접근하여도 스코프에 변수가 존재하기 때문에 에러가 발생하지 않는다. 다만 undefined를 반환한다. 이후 변수 할당문에 도달하면 비로소 값이 할당된다.
let
으로 선언된 변수는 선언 단계와 초기화 단계가 분리되어 진행된다. 즉, 스코프에 변수를 등록(선언 단계)하지만 초기화 단계는 변수 선언문에 도달했을 때 (코드 실행 후) 이뤄진다. 초기화 이전에 변수에 접근하려고 하면 참조 에러가 발생한다. 이는 아직 변수가 초기화되지 않았기 때문이다. 즉, 변수를 위한 메모리 공간이 아직 확보되지 않았기 때문이다. 따라서 스코프의 시작 지점부터 초기화 시작 지점까지는 변수를 참조할 수 없다. 스코프의 시작 지점부터 초기화 시작 지점까지의 구간을 일시적 사각지대 (Temporal Dead Zone; TDZ) 라고 부른다.
use strict
는 자바스크립트의 구문이 실행되는 환경을 설정하는 예약어이다. ECMAScript 5에서 처음으로 소개된 strict
모드는 자바스크립트 코드에 더욱 엄격한 오류 검사를 적용한다. strict
모드는 스크립트 or 개별 함수 맨 처음에 "use strict
" 지시어를 사용하여 선언한다.
<script>
"use strict";
var v = "Hi! I'm a strict mode script!";
</script>
function strict(){
// Function-level strict mode syntax
'use strict';
function nested() { return "And so am I!"; }
return "Hi! I'm a strict mode function! " + nested();
}