1. 변수 구분, 글로벌 변수 오해, use strict 사용
변수 구분
•
로컬(지역) 변수, 글로벌(전역) 변수로 구분할 수 있습니다.
변수를 구분하는 이유는?
변수의 기능과 목적이 다르기 때문입니다.
글로벌 변수의 기능 및 목적은 다른 JS파일에서 변수값을 공유하고 파일에서 공통 변수 개념으로 사용할 수 있다는 것입니다. 이는 좋아보이고 사용하는데는 편리할 수 있지만 처리 속도가 떨어집니다.
로컬 변수의 기능 및 목적으로 빠르게 식별자를 해결하기 위해 가까운 스코프의 변수를 사용하려는 것입니다.
글로벌 변수 오해
•
글로벌 변수는 글로벌 오브젝트의 로컬 변수입니다.
•
var value = 100처럼 var 키워드 사용이 정상입니다. 그런데 여기서 var 키워드를 작성하지 않으면 글로벌 변수로 간주하는데 이것이 문제가 될 수 있습니다.
⇒ var 키워드를 사용하지 않고 value를 글로벌 변수에 선언 후 100을 할당한 코드입니다. point function에서 value에 300을 할당 후 출력을 하는데 이 때 value는 로컬 변수가 아니므로 글로벌 오브젝트의 value 변수에 300을 할당하게 됩니다.
이처럼 함수 안에서 글로벌 변수에 값을 설정하는 것은 좋은 목적이 아닙니다.
설계를 할 때 로컬 변수와 글로벌 변수를 구분한 목적을 고려해야 합니다.
//"use strict"
value = 100;
function point(){
value = 300;
console.log("함수: ", value);
};
point();
JavaScript
복사
[실행 결과]
함수: 300
use strict 사용
"use strict"
function point(){
try{
value = 300;
console.log("함수: ", value);
} catch(e){
console.log("글로벌 변수 사용 불가");
}
};
point();
JavaScript
복사
ES5에서 var 키워드를 사용하지 않을 경우 에러가 발생하도록 'use strict'모드를 추가했습니다.
하지만, 근본적인 접근방법은 아닙니다.
ES6+에서는 "use strict"가 기본환경입니다. (전체는 아닙니다.)
2. let 변수 개요, let 변수 선언, 블록 스코프
let변수 개요
•
let book = "책"
◦
블록(block)스코프를 가진 변수
◦
변수가 선언된 블록이 스코프
•
스코프 적용 기준
◦
블록{ }, 문, 표현식
•
블록 { }안과 밖이 스코프가 다릅니다.
◦
변수 이름이 같아도 값이 대체되지 않음.
let sports = "축구";
if(sports){
let sports = "농구";
console.log("안:", sports);
};
console.log("밖: ", sports);
JavaScript
복사
[실행 결과]
안: 농구
밖: 축구
let 변수 선언
•
Syntax
◦
let name1 [= value1] [, name2 [= value2] ]
let value1;
let value2 = "테스트";
let value3 = "테스트2", value4;
let value3 = "테스트2", value4 = "테스트3";
JavaScript
복사
•
name1, name2에 변수 이름 작성
◦
식별자로 사용합니다.
◦
[ ] 는 생략 가능을 나타냅니다.
◦
값을 할당하지 않아도 됩니다.
let value1 = (10 + 20);
JavaScript
복사
•
value1, value2에 초기값 작성
◦
표현식 작성이 가능하고 평가 결과 사용도 가능합니다.
블록 스코프
•
블록 기준으로 값이 정의되며 블록 안과 밖의 변수는 구분됩니다. 즉 같은 이름이지만 각각 다르게 존재할 수 있습니다.
◦
function name(){ 코드 }
◦
if(a===1){ 코드 }
•
블록 안과 밖의 스코프가 다르기에 변수 이름이 같아도 값이 대체되지 않습니다.
•
스코프에 같은 이름을 사용하는것을 불가능합니다.
3. 블록 스코프 유형: function 블록, try-catch, switch-case
function 블록
•
function name(){ }도 블록 스코프입니다.
•
function 안과 밖에 같은 이름의 let변수 선언이 가능합니다.
스코프가 다르기 때문입니다.
let sports = "축구";
function show(){
let sports = "농구"
console.log("안:",sports);
};
show();
console.log("밖:",sports);
JavaScript
복사
•
function 밖의 let 변수를 function 안에서 사용 가능합니다(클로저)
let sports = "축구";
function show(){
console.log(sports);
};
show();
JavaScript
복사
try-catch
let sports = "축구";
try{
let sports = "농구";
console.log("안:", sports);//안: 농구
abc = error;
} catch(e){
console.log("catch: ", sports); //catch: 축구
};
console.log("밖: ",sports);//밖: 축구
JavaScript
복사
•
try-catch문도 블록 스코프 이며 try블록 { }기준으로 안과 밖에 같은 이름의 let변수 선언이 가능합니다.
•
catch()에서 try밖의 변수 사용합니다.
switch-case
let item = 1;
switch (item){
case 1:
let sports;
break;
case 2:
//let sports; //주석 해제시 컴파일 에러 발생
default:
console.log(sports);
};
JavaScript
복사
•
switch 문도 블록스코프입니다.
•
switch 블록 기준으로 같은 이름의 let 변수 작성이 불가능합니다.
◦
이때 case, default는 블록 스코프가 아닙니다.
4. let 변수와 var 변수 차이
차이점
•
for()문에서 반복할 때마다 var 변수는 스코프를 갖지 않는 반면 let 변수는 스코프를 가집니다.
var 변수와 스코프
<ul class="sports">
<li>축구</li>
<li>농구</li>
<li>야구</li>
</ul>
<input id="output" type="text" readonly>
HTML
복사
var node = document.querySelector(".sports");
var $output = document.querySelector("#output");
for(var k = 0; k< node.children.length; k++){
node.children[k].onclick = function(event){
event.target.style.backgroundColor = "yellow";
$output.value = k;
};
};
JavaScript
복사
•
어떤 것을 클릭해도 3이 출력되는 것을 확인할 수 있습니다. 이는 var k = 0;에서 k변수의 스코프는 함수 전체이기 때문에 세가지 li 요소들이 하나의 k변수를 가지게되고 for문이 끝날때 할당 된 3을 출력하게 되는 것입니다.
let변수와 스코프
var node = document.querySelector(".sports");
var $output = document.querySelector("#output");
for(let k = 0; k< node.children.length; k++){
node.children[k].onclick = function(event){
event.target.style.backgroundColor = "yellow";
$output.value = k;
};
};
JavaScript
복사
•
var k =0;을 let k =0으로 바꾸었습니다. 이제 이벤트 발생시 그 때의 k값을 출력합니다.
이를 통해 k는 블록단위로 가져가며 구분되는것을 확인할 수 있습니다.
이는 장점만 있는것은 아닌데, 만일 반복문의 반복 횟수가 수천 수만번이 넘어가게 될 때 let변수로 설정하면 각각의 블록이 메모리를 차지하게도면서 퍼포먼스가 떨어질 수 있습니다.
5. let변수와 this
•
글로벌 오브젝트에서 let변수를 this로 참조하는것은 불가능합니다.
⇒ var music = "음악"은 window 오브젝트에 설정됩니다.
⇒ let sports = "축구";는 window 오브젝트에 설정되지않습니다.
⇒ this.music은 글로벌 오브젝트 window에 설정되어 출력이 되지만, sports는 window에 설정되지 않았기에 undefined가 출력됩니다.
var music = "음악";
let sports = "축구";
console.log(this.music, this.sports);
JavaScript
복사
[실행 결과]
음악, undefined
글로벌 오브젝트에서 var와 let변수가 설정되는 위치 구조
•
globalVar 는 window객체의 globalVar에 등록되는것을 확인할 수 있습니다.
•
globalLet 는 window 객체에서 찾을 수 없고 Script 라는 영역에 저장된 것을 확인할 수 있습니다.
◦
이는 스펙에 정의된 이름입니다.
•
그렇기에 this.globalVar는 window객체에서 globalVar프로퍼티를 찾을 수 있기에 '글로벌' 이 출력되지만, globalLet 은 글로벌 오브젝트에 등록되지 않았기에 undefined가 출력됩니다.
6. js파일 다수 사용
다수의 js파일 사용
•
모든 js파일에서 글로벌 오브젝트에 작성한 var 변수와 let변수를 공유할 수 있습니다.
◦
단, 블록 안에 작성한 것은 공유하지 않습니다.
var globalVar = "var변수";
var globalLet = "let변수":
JavaScript
복사
console.log(globalVar);
console.log(globalLet);
JavaScript
복사
위처럼 두 개의 js파일을 사용하면서 1번 js에서 선언한 변수들을 2번 js에서 사용이 가능합니다.
그리고, 글로벌 오브젝트에 작성했지만 공유하고 싶지가 않을 경우 블록을 사용하면 글로벌 오브젝트의 지역변수로 사용할 수 있습니다.
{ let globalBlock = "block의 let변수"; }
JavaScript
복사
console.log(globalBlock)
JavaScript
복사
함수 내 변수들의 스코프 분석
블록의 형태
•
Block
•
Local
•
Script
•
Global
정리
•
글로벌 오브젝트에 작성하면
◦
var 변수: window에 설정되고 공유됩니다.
◦
let 변수: Script에 설정되고 공유됩니다. (다만 설정되는 영역이 Script입니다.
▪
window.sports = { }처럼 의도적으로 하지 않아도 됩니다.
◦
{ let 변수 }: Block에 설정, 공유하지는 않습니다.
▪
글로벌 오브젝트에서만 사용하는 로컬 변수로 사용합니다.
var globalBal = "var 변수";
let globalLet = "let 변수";
{
let globalBlock = "block 변수";
};
JavaScript
복사
•
함수에 작성
◦
var 변수, let변수: Local
◦
{ let 변수 }: Block
7. 호이스팅
호이스팅
•
ES5의 실행 콘텍스트 처리 순서
1.
함수 선언문 설정
2.
변수 이름을 바인딩
변수값은 undefined
3.
소스 코드 실행
⇒ 출력 코드(console.log)아래에 var music = "음악"이 있습니다. 이렇게 변수가 아래에 위치하지만 식별자 해결을 할 수 있습니다. 하지만 이 때 music의 값은 undefined입니다.
이것을 호이스팅이라 하는데 식별자 해결을 하지 못하면 에러가 발생합니다.
console.log("music 변수:", music);
var music = "음악";
JavaScript
복사
[실행 결과]
music 변수: undefined
•
let변수는 호이스팅(Hoisting)되지 않음
◦
즉, let변수 앞에서 변수 사용이 불가능
try{
console.log("music 변수:", music);
} catch(e){
console.log("에러가 발생했습니다");
}
let music = "음악";
JavaScript
복사
[실행 결과]
에러가 발생했습니다.
let 변수를 인식하는 시점
//변수가 모두 아래에 작성되어 있습니다.
console.log(globalVar);//undefined
var globalVar = "var 변수";
JavaScript
복사
1. console.log에 undefined가 출력됩니다.
2. 글로벌 오브젝트 Global(window)를 살펴보면 globalVar 변수값이 undefined이지만 선언되 있습니다.
3. var globalBVar = "var 변수";
•
이 부분에서 undefined였던 globalVar의 초기값(undefined)이 'var 변수' 로 할당됩니다.
try{
console.log(globalLet);
} catch(e){
console.log("globalLet 인식 불가);
}
let globalLet;
console.log(globalLet);
JavaScript
복사
1.
아래의 globalLet을 인식하지 못하기에 에러가 발생합니다. (globalLet 인식 불가 출력)
2.
let globalLet;
•
이때 Script영역에 globalLet이 표시됩니다. 즉, 변수 선언을 실행해야 표시됩니다.
•
값을 할당하지 않고 변수를 선언만 하면 엔진이 undefined를 할당합니다.
3.
console.log(globalLet);
•
let 변수는 변수 선언을 실행한 후에 변수를 인식할 수 있습니다.
•
즉, 식별자를 해결할수 있어서 undefined를 출력합니다.
block안에 let변수 작성
{
console.log(variable);
var variable = "var 변수";
let blockLet = "let 변수";
}
JavaScript
복사
1.
console.log(variable);
•
글로벌 오브젝트(Global(window))를 살펴보면 variable의 변수값이 undefined이지만 등록되어있습니다.
2.
blockLet 변수도 undefined로 표시됩니다. 하지만, 호이스팅을 사용할 수는 없습니다.
3.
앞에서 글로벌 변수는 Script에 변수가 표시되지 않습니다. 하지만, block은 이 때 표시가 됩니다. 즉, 엔진이 블록 내부를 엔진이 한 번 훑었다는 의미가 됩니다.
4.
let 변수가 별도의 영역(block)에 설정되는 개념을 MDN에서는 "temporal dead zone"으로 기술합니다.
→ES6 스펙에 작성된 용어는 아닙니다.
5.
temporal 에서 let변수가 undefined인 상태를 나타내는 뉘앙스가 풍기며 dead zone에서 let 변수에 값을 할당한 후에는 임시 상태가 해제되어 변수를 사용할 수 있다는 뉘앙스를 풍깁니다.
즉, let blockLet = "let 변수"; 이 실행된 후에는 변수를 사용할 수 있다는 의미가 됩니다.
정리
1.
개념적으로 접근할 때 초기화 단계(코드를 실행하기 전)에서 정적 환경의 선언적 환경 레코드에 변수 이름을 바인딩합니다. 이때, var 변수는 undefined를 초기값으로 설정하고 let 변수는 초기값을 설정하지 않습니다. 엔진에서는 이런 처리를 초기화자(Initializer)로 구분하고 있습니다.
2.
변수 이름으로 식별자를 해결할 때
•
변수에 값이 있으면 변수로 인식하고
•
변수에 값이 없으면 변수로 인식하지 않는개념입니다.
3.
let변수 선언을 실행하면 그때 값이 설정되며
•
값을 할당하지 않고 선언만 하면 엔진이 undefined를 할당합니다.
•
하지만, let변수에서 undefined는 표시를 위한 것이지 엔진 관점에서는 값을 가지고 있지 않은 상태입니다.
그렇기 때문에 호이스팅이 불가능합니다.
4.
따라서, 변수 선언을 실행한 후에는 변수가 값을 갖고 있으므로 변수를 인식할 수 있습니다.
5.
엔진에서는 초기화자(Initializer)와 Binding List 메커니즘을 사용합니다.
8. const 변수
변경 불가능한 변수 선언
구문: name1 [=value1][, name2 [=value2]]
•
name1에 변수 이름 작성, 식별자로 사용
•
value1, value2에 초기값 작성
◦
반드시 값을 작성해야 하며 변수선언만 할 수 없습니다.
◦
표현식 작성 사용가능하며 평가 결과를 사용합니다.
•
JS에서 상수는 대문자 사용이 관례입니다.
•
우선 let이 아닌 const사용을 검토해야 합니다.
const bonus = 100;
const POINT = 200;
JavaScript
복사
const sports = "축구";
try{
sports = "농구";
} catch(e){
console.log("const 할당 불가");
};
JavaScript
복사
[실행 결과]
const 할당 불가
•
const 변수전체를 바꿀 수는 없지만 Object의 프로퍼티 값을 바꿀수는 있습니다.
⇒book에 값을 할당하면 const이기 때문에 에러가 발생합니다.
하지만 book내부의 title 프로퍼티 값은 변경할 수 있습니다.
const book = {title: "책"};
try{
book = {title: "음악 책"};
}catch(e){
console.log("const 전체 할당 불가");
}
book.title = "미술 책";
console.log(book.title);
JavaScript
복사
[실행 결과]
const 전체 할당 불가
미술 책
•
배열의 엘리먼트 값도 바꿀 수 있습니다.
const book = ["책"];
try{
book = ["음악 책"];
}catch(e){
console.log("const 전체 할당 불가");
}
book[0] = "미술 책";
console.log(book[0]);
JavaScript
복사
[실행 결과]
const 전체 할당 불가
미술 책