범위: 전역 범위(함수 외부)/ 함수 범위(함수 내부)로 지정하며 같은 범위 내에선 재선언, 값 업데이트 모두 가능합니다. (함수 스코프 적용)
값 업데이트 전 접근 시 : undefined
console.log(a); //undefined
var a = 1;
console.log(a); // 1
위와 같이 에러가 발생하지 않고 정의되지않음(undefined)로 값이 출력됩니다.
이러면 에러 안나고 돌아가고 좋지 않나? 라고 생각할 수 있는데 그러면 경기도 오산이에요.
var student = "yuni";
var teacher = "elice";
if(student.length >= 3) {
var student = "elice"
}
console.log(student) //elice
이렇게 의도치 않게 학생은 유니에서 엘리스가 되어버렸습니다. 코드가 길어져 다른 블록에서 같은 변수명을 쓸 시 돌아올 수 없는 강을 건너게 되는 것이죠.
ES6에서 나온 var 의 문제점의 개선을 반영한 변수 키워드입니다.
블록과 함수 스코프 안에서 선언시 해당 블록/함수 안에서만 사용 및 업데이트가 가능합니다. 단, 재선언은 안됩니다. (var 문제 때문에 등장한 변수키워드니 되면 당근 안되겠죠?)
//case 1
console.log(a); //reference error
let a = 1;
console.log(a); // 1
//case 2
function bCheck() {
let b = 2;
console.log(b); //2
}
console.log(b) // reference error
그럼 변수값 업데이트와 재선언은요?
//case 1
let a = 1;
if(a === 1) {
let a = 3;
console.log(a); //3
}
console.log(a) //1
//case 2
let a = 1;
if(a === 1) {
a = 3;
console.log(a); //3
}
console.log(a) //3
이렇게 작동합니다. let의 스코프는 블록이기 때문에 if문 블록 안에서 재선언을 해도 블록 밖에선 아무런 소용이 없다 이말이에요~~!
하지만 업데이트의 경우는 다릅니다. 전역변수를 고-대로 가져다 업데이트만 하시는 경우엔 당연하겠지만 전역변수의 값이 업데이트 됐으므로 밖에서나 안에서나 업데이트 한 값을 리턴합니다.
const도 let과 함께 ES6에서 나온 아주 엄격한 친구입니다. var에 당한 개발자가 치를 떨고 만들었을까요? let 보다 더 엄격한 것이 특징입니다. 업데이트, 재선언 모두 불가합니다. 오직 객체의 경우만 가리키는 값을 변경할 수 있습니다.(직접적인 변경이 아닌 가리키는 포인트가 변경된 거라고 이해했습니다.)
//case 1
console.log(a); //reference error
const a = 1;
console.log(a); // 1
//case 2
function bCheck() {
const b = 2;
console.log(b); //2
}
console.log(b) // reference error
//case 3
const c = 1;
if(c === 1) {
const c = 3;
console.log(c); //3
}
console.log(c) //1
//case 4
const d = 1;
if(d === 1) {
d = 3;
console.log(d); //TypeError
}
console.log(d) // 1
case 1~3까지는 let과 동일합니다. const도 블록 스코프이기 때문에 함수 포함 모든 블록 안에서의 스코프는 또 다른 세계라고 인식합니다. 그렇기에 재선언 같아 보이지만 재선언이 아닌 case 3이 가능할 수 있었던 거죠. 다만 업데이트는 불가합니다. 바로 타입에러로 업데이트 할 생각은 꿈도 꾸지 말라고 하네요.
위 케이스 3과 같은 경우를 변수 쉐도잉이라고 합니다.
변수 쉐도잉(Variable Shadowing)은 하나의 스코프에서 이미 선언된 변수와 같은 이름을 변수명으로 다시 선언하는 것을 의미합니다. 이 경우, 기존에 선언된 변수는 새로운 변수에 의해 가려져서 접근할 수 없게 됩니다.
예를 들어, 아래의 코드에서는 x 변수가 선언된 블록 내에서 let x = 2에 의해 새로운 변수로 다시 선언되어 변수 쉐도잉이 발생합니다.
let x = 1;
if (x === 1) {
let x = 2;
console.log(x); // 2
}
console.log(x); // 1
블록 외부, 즉 전역에서 console.log(x)를 호출하면 전역 변수 값인 1이 출력됩니다. 다른 세계에 살고있는 x이니까요. 하지만 블록 안 세계는 또 다른 세계죠. 그러니 블록 안 세계의 x의 값은 2입니다. 따라서 블록 내부에서의 console.log(x)는 2를 출력하는 것이죠.
변수 쉐도잉은 코드의 가독성을 해치기 때문에 가능하면 피하는 것이 좋습니다. 특히, 함수 내부에서 매개변수와 같은 이름으로 변수를 선언하면 함수 호출 시 매개변수와 같은 이름으로 변수를 전달했을 때 변수 쉐도잉이 발생할 수 있습니다. 이래서 변수명 짓다 기절이라는 밈이 생겼나 봅니다.