Scope 변수 참조의 유효범위로 전역 스코프
, 지역스코프
와 나뉘어 지는데, 지역 스코프
는 안에서 추가로 함수 레벨 스코프
, 블록 레벨 스코프
로 나뉘어 진다.
개발자 모드의 source 탭에서 확인이 가능하다.
전역 스코프 (global)
전역 스코프와 지역스코프 안에서 모두 사용 가능
지역 스코프 (Local)
함수 레벨 스코프
const sum = function () {}
script
라는 곳에 생성
블록 레벨 스코프
if(){}
block
라는 곳에 생성
함수 레벨 스코프와 블록 레벨 스코프의 내부를 Local이라고 한다.
스코프 체인 (scope chain)
스코프를 타고타고 올라가서 할당된 변수를 찾는 것을 의미한다. 글로벌 스코프 안에 지역스코프 안에 지역스코프가 생길 수 있다. 즉, 다른 스코프안에 스코프들을 생성할 수 있다.
변수 var
블록 레벨 스코프 무시가 가능하여, 밖에서 참조 가능 하기 때문에 사용을 지양한다.
클로져
전역변수가 많으면 어디에서든 실수로라도 접근을 할 수 있기 때문에 최대한 전역변수를 줄여서 코드를 해야한다.
클로저는 내부에 선언된 함수가 외부함수의 지역변수를 사용해 줬을 때만 클로저라고 선언된다.
은닉화
클로저를 이용하여 내부 변수와 함수를 숨길 수 있다.
function Counter(){ let privateCounter = 0; function changeBy(val) { privateCounter += val; } return { increment: function () { changeBy(1); }, decrement: function () { changeBy(-1); }, value: function () { return privateCounter; }, }; } const counter = Counter(); console.log(counter.value()); // 0 counter.increment(); counter.increment(); console.log(counter.value()); // 2 counter.decrement(); consoel.log(counter.value()); // 1
코드예시
let txtField = document.querySelector('.txt'); let toggleBtn = document.querySelector('.toggle'); let toggle = (function () { let isVisable = false; // 1.클로저를 반환 return function () { txtField .style.display = isVisable ? 'block' : 'none'; // 3. 상태 변경 isVisable = !isVisable; }; })(); // 2. 이벤트 프로퍼티에 클로저를 할당 toggleBtn.onclick = toggle;
코드 설명
즉시 실행함수가 함수를 반환한 뒤 바로 소멸된다.
이때 반환된 힘수는 자신이 생성됐을때의 Lexical 환경
에 속한 변수인 isVisable
을 기억하는 클로저
가 된다.
클로저가 기억하는 변수인 isVisable
은 txt 표시 상태를 나타낸다. 클로저를 버튼 이벤트 프로퍼티에 할당을 했다. 이제 이 클로저를 제거하지 않는한 isVisable
은 소멸되지 않는다.
버튼 클릭시 클로저가 호출되게되는데 이때 txt요소의 표시 상태를 나타내는 변수인 isVisable
값이 변경이 되게 된다.
이 변수는 클로저에 의해 참조 되고 있기 때문에 변경된 최신 값을 계속 유지할 수가 있다.
클로저를 잘 알아야하는 이유는 유용하게 사용하기보단 알기 힘든 버그를 잘 수정하기 위해서이다.
에러코드
출력 결과로 12345가 아닌, 55555가 나온다.
function counting() { let i = 0; for (i = 0; i < 5; i += 1) { setTimeout(function () { console.log(i); }, i * 100); } }
해결방법 1
즉시 실행 함수를 이용한다. 루프마다 클로저 생성
function counting() { let i = 0; for (i = 0; i < 5; i +=1) { setTimeout(function () { console.log(number); }, number * 100); } }
해결방법 2
let을 이용한다. let은 블록수준 스코프기 때문에 매 루프마다 클로저가 생성된다.
function counting() { for (let i = 0; i < 5; i += 1) { setTimeout(function () { console.log(i); }, i * 100); } }
코드를 한 번 해석하는 과정을 거치는데, 그때 선언 단계가 위로 올라가게된다. 선언부가 위로 올라 가고 초기화 단계까지 실행되는 형태를 호이스팅이라고 한다. 이렇게 될 경우에는 값이 undefined
로 잡혀 에러를 미리 방지 할 수 없다.
변수 선언 단계
선언단계
선언한 변수를 식별자(변수명)가 담기는 객체에 할당하는 단계
TDZ (Temporal Dead Zone)
선언단계와 초기화단계 사이에 있는 구역이며, Let과 Const는 선언단계와 초기화단계가 분리 되어 TDZ에 머무르게 된다. 할당되는 변수를 만나기 전까지 TDZ에 있음
초기화단계
변수에 할당할 메모리 공간을 부여하는 단계
변수var
선언과 초기화단계 둘 다 되어버리다. 메모리를 할당 받아 버려 에러를 검출하기 어렵다.
할당단계
정의된 변수에 데이터가 할당되는 단계
함수 선언식
선언, 초기화, 할당까지 되어버린다.
function fn1() {}
실행 컨텍스트(Execution context)는 우리가 작성한 코드가 실행되는 환경을 말하며, scope, hoisting, this, function, closure 등의 동작원리를 담고 있는 자바스크립트의 핵심원리를 말한다.
글로벌 실행 컨텍스트 (Global Execution Context)
코드가 실행되기 전에 생성이 되며, 함수 내에 없는 코드는 모두 전역 실행 컨텍스트 안에 존재한다.
자바스크립트 엔진은 일부 자바스크립트 코드를 실행할 때마다 글로벌 실행 컨텍스트(Global Execution Context)를 작성한다.
글로벌 실행 컨텍스트의 특징으로는 무조건 하나의 전역 실행 컨텍스트 만이 존재하며, 애플리케이션이 종료될 때 (웹 페이지에서 나가거나 브라우저를 닫을 때)까지 유지하는 것이다.
함수 실행 컨텍스트(Functional Execution Context)
전역 실행 컨텍스트가 생성된 후, 함수가 실행(ex 호출) 될 때마다 새로운 실행 컨텍스트가 작성된다.
console.dir()을 사용하면, 개발자 도구로 쉽게 확인이 가능하다.
자기 자신의 스코프(scope)를 제외한 자신과 가장 가까운 변수 객체의 스코프 순으로 접근하는 것을 눈으로 확인할 수 있다.
참고자료
코딩의 성지