변수 접근 규칙에 따른 유효 범위로, 중괄호(블록) 또는 함수에 의해 나누어진다.
안쪽에 변수가 선언되었는가, 바깥쪽에 변수가 선언되었는가에 따라 변수 접근 가능여부가 달라진다.
let username = 'kimcoding';
if (username) {
let message = `Hello, ${username}!`;
console.log(message); // Hello, kimcoding!
}
console.log(message); // message is not defined
4번째 줄에서 message를 출력할 때는, 3번째 줄의 username을 바깥 스코프에서 가져왔으므로 정상적으로 출력된다.
6번째 줄에서는 message라는 변수 자체가 안쪽 스코프에 선언되어 있으므로 바깥쪽에서는 접근할 수 없어 ReferenceError를 낸다.
let greeting = 'Hello';
function greetSomeone() {
let firstName = 'Josh';
return greeting + ' ' + firstName;
}
console.log(greetSomeone()); // Hello Josh
console.log(firstName); // firstName is not defined
greeting 변수는 바깥 스코프에 정의되어 있으므로 함수 안쪽에서 사용할 수 있다. 따라서 greeting 변수와 firstName 변수의 조합에 의해 'Hello Josh' 문자열이 출력된다.
firstName 변수는 안쪽 스코프에 선언되어 있으므로 바깥쪽에서는 접근이 불가능해 ReferenceError를 낸다.
중첩이 가능하며, 가장 바깥의 스코프는 전역 스코프(Global scope)라고 부른다.
전역이 아닌 다른 스코프는 전부 지역 스코프(local scope)
지역 변수는 전역 변수보다 더 높은 우선순위를 가진다.
let name = '김코딩'; // 전역 변수
function showName() {
let name = '박해커'; // 지역 변수
console.log(name); // 박해커
}
console.log(name); // 김코딩
showName();
console.log(name); // 김코딩
첫 번째와 세 번째 출력은 전역 변수로 선언된 name
을 가져온다.
(showName
함수 안쪽에 선언된 지역 변수 name
은 안쪽 스코프이므로 접근이 불가능함)
두 번째 출력(showName();
)은 함수 안에서 선언한 name
이라는 지역 변수에 접근하고 있다.
변수 이름이 전역 변수와 똑같지만, 지역 변수가 전역 변수보다 우선순위가 높으므로, 지역 변수 name
이 출력된다.
동일한 변수 이름으로 인해 바깥쪽 변수가 안쪽 변수에 의해 가려지는(shadow) 이러한 현상을 쉐도잉(variable shadowing)이라고 부름
let name = '김코딩';
function showName() {
name = '박해커'; // let 키워드를 이용해 선언하지 않았으므로, 첫 번째 줄의 전역 변수 name을 사용 → 이때, 전역 변수 name의 값이 바뀜
console.log(name); // 박해커
}
console.log(name); // 김코딩
showName();
console.log(name); // 박해커
위의 예제와 다르게 세 번째 줄에서 let
키워드를 이용해 선언하지 않았다.
‘박해커’라는 값으로 할당하고 있는 name
변수는 전역에 선언된 name
변수를 그대로 사용하겠다는 의미
지역 스코프에서 새로 선언되지 않으면 같은 변수이다.
→ showName
함수가 실행되기 전, 첫 번째 출력에서는 ‘김코딩’을 출력하고, 그 이후에는 전역변수 name의 값이 바뀌기 때문에 두 번째와 세 번째 출력에 ‘박해커’가 출력된다.
스코프의 종류에는 두 가지가 있다.
블록 스코프(block scope): 중괄호를 기준으로 범위가 구분됨. (조건문이나 반복문의 중괄호도 포함)
함수 스코프(function scope): 함수로 둘러싼 범위. function 키워드가 등장하는 함수 선언식 및 함수 표현식은 함수 스코프를 만든다.
주의: 화살표 함수는 블록 스코프로 취급됨
블록 스코프 안에서 정의된 변수는 블록 범위를 벗어나는 즉시 접근이 불가하다.
for (let i = 0; i < 5; i++) {
console.log(i); // (다섯 번 반복)
}
console.log('final i:', i); // i is not defined
위의 예제의 세 번째 줄에서 출력하려는 변수 i는 블록 범위를 벗어났기 때문에 ReferenceError가 나옴
조건문 내의 변수 선언을 let 키워드가 아닌 var 키워드를 이용하면 어떻게 될까?
for (var i = 0; i < 5; i++) {
console.log(i); // (다섯 번 반복)
}
console.log('final i:', i); // final i: 5
결과는 5로 잘 나온다. 왜?
여기서 var 키워드는 for 문이 만들어낸 블록 스코프를 무시하기 때문이다.
var
와 let
var
키워드로 정의한 변수는 블록 스코프를 무시하고, 함수 스코프만 따른다. (화살표 함수의 블록 스코프는 무시하지 않음)
보통 코드를 작성할 때 블록은 들여쓰기가 적용되고, 그 구분이 시각적으로 분명하기 때문에 대다수의 사람들은 블록 스코프를 기준으로 코드를 작성한다.
블록 단위로 스코프를 구분했을 때, 훨씬 더 예측 가능한 코드를 작성할 수 있으나, var
는 이 규칙을 무시하기 때문에 비교적 예측이 어렵다.
또한, let
키워드는 재선언을 하면 에러를 내 재선언을 방지해주나 , var
키워드는 재선언을 해도 아무런 에러가 나지 않아 재선언 방지가 되지 않는다.
이러한 점 때문에 var
키워드보다 let
키워드의 사용이 권장된다.
const
변하지 않는 값, 상수(constant)를 정의할 때 이용한다.
const
는 값의 재할당이 불가능하다. 값을 재할당할 경우 TypeError를 내므로, 의도하지 않은 값의 변경을 막을 수 있다.
브라우저에는 window라는 객체가 존재함
전역 변수는 최소화 하자
let, const를 주로 사용하기
선언 없는 변수 할당 금지
function showAge() {
age = 90;
console.log(age); // 90
}
showAge();
console.log(age); // 90
console.log(window.age); // 90
‘use strict’
라고 입력하면 사용 가능