[JS] 스코프와 클로저

dee·2022년 10월 12일

javascript

목록 보기
1/3
post-thumbnail

스코프 (scope)

  • 식별자가 유효한 범위 또는 식별자를 검색할 때 사용하는 규칙.

  • 변수나 함수, 함수의 매개 변수 등과 같은 식별자는 선언된 위치가 중요.
    => 식별자 참조 여부가 결정됨.

  • 스코프를 통해 식별자의 이름 충돌을 방지할 수 있음.
    => 다른 스코프 내에서 같은 이름의 변수 사용 가능. (네임 스페이스)
    🚨 자바스크립트는 같은 스코프내에 중복 선언을 허용하는 var와 같은 예외가 있음.

  • 전역 / 지역 스코프

    정의참조
    전역코드 가장 바깥 영역전역 변수는 어디서든지 참조
    지역함수 몸체 내부지역 변수는 자신의 스코프와 하위 스코프에서 참조 가능
    스코프 체인으로 참조할 변수를 검색
  • 스코프 체인
    스코프가 상위 스코프와 하위 스코프를 갖으며 단방향으로 계층적으로 연결된 것.
    => 함수의 중첩으로 계층적 구조를 갖음.
    🚨 단방향이므로 상위에서 하위 스코프의 변수를 참조할 수 없음

  • 함수 레벨 스코프
    지역 스코프라고도 할 수 있으며 자바스크립트의 var로 선언된 변수는 함수 코드 블럭만을 지역 스코프로 인식.
    🧷 let과 const는 블록 단위로 지역 스코프를 생성.

📍아래 예제를 통해 var 변수 스코프에 대해 살펴보고 문제점을 확인하자.

// 전역에 var 변수 선언 및 할당.
var color = 'red'; 

if(typeof color === 'string'){
  // 블럭문 안에서 var 변수 선언 및 할당. 
  var color = 'blue';
}

console.log(color); // blue
  1. 전역에 var로 color 변수 선언 및 red 할당.
  2. 조건문(블럭문) 안에서 똑같은 color라는 변수를 var를 통해 선언 및 할당.
  3. 함수 스코프를 가지는 var 변수가 블럭문 안의 color로 중복 선언되었음.
  4. 전역의 color는 blue라는 값으로 재할당.
  5. console.log의 color는 blue가 출력됨.

🧐 함수 레벨 스코프를 가지는 var로 선언된 변수는 위와 같이 블록 레벨 스코프를 인정하지 않아 전역변수로 재할당된다. 이는 의도치 않은 할당이 일어난 것이므로 코드를 구현하면서 예상치 못한 문제에 직면할 수 있음을 알 수 있다.


렉시컬 스코프 (정적 스코프)

  • 자바스크립트는 렉시컬 스코프를 따름.
  • 렉시컬 스코프는 함수가 정의된 시점에 상위 스코프를 정적으로 결정.
    🧷 함수 호출된 시점에 따라 상위 스코프를 결정하는 것은 동적 스코프
  • 함수는 자신의 내부 슬롯 [[Environment]]에 자신이 정의된 환경(상위 스코프)를 저장.
// 변수 선언 및 할당
const color = 'coral';

// 함수 선언문
function outerFunc(){
	const color = 'skyblue';
    innerFunc();
}

// 함수 선언문
function innerFunc(){
	console.log(color);
}

outerFunc(); // coral
innerFunc(); // coral
  1. 전역에 color 변수 선언 및 할당.
  2. 각 함수 선언문으로 함수 정의.
  3. outerFunc을 호출.
  4. outerFunc안에 중첩된 innerFunc이 호출.
  5. innerFunc에는 color 변수가 없음.
  6. innerFunc의 외부 참조를 살펴보니 정의된 시점에 상위 스코프는 전역임.
  7. 전역의 color 변수 coral값을 출력.
  8. outerFunc에 중첩된 innerFunc 종료 후 outerFunc 종료.
  9. 전역의 innerFunc 호출.
  10. 중첩된 innerFunc과 똑같이 외부 참조가 전역임.
  11. 전역의 color 변수 coral값을 출력 후 종료.

🧐 outerFunc에 중첩된 innerFunc이 outerFunc의 color를 참조할 것이라고 예상할 수도 있을 것이다. 하지만 자바스크립트는 함수의 정의 시점에 스코프를 결정함으로 호출 시점과 상관없다는 것을 기억하자!


클로저

  • 외부 함수보다 중첩 함수의 생명 주기가 길며 중첩 함수는 이미 종료된 외부 함수의 변수를 참조.
    🚨 위 두가지 조건을 모두 충족하는 것을 일반적으로 클로저라고 함.
  • 외부 함수의 실행 컨텍스트는 스택에서 제거되지만 렉시컬 환경까지 소멸하지 않음.
    => 중첩 함수가 참조하고 있기 때문.
  • 자유 변수는 클로저에 의해 참조되는 상위 스코프의 변수.
  • 상태를 안전하게 변경하고 유지하기 위해 사용. (캡슐화와 정보 은닉)
// 즉시 실행 함수(IIFE)
const color = (function(){
	let color = 'red';
  	
  	const init = () => {
    	color = 'red';
      	return color;
    }
  	
    const setColor = newColor => {
    	color = newColor;
      	return color;
    }
    
    return { init, setColor }
})();

// 반환된 객체 값이 color1 변수에 할당.
const color1 = color;

console.log(color1.setColor('purple')); // purple
console.log(color1.init()); // red
  1. 즉시 실행 함수 호출
  2. 반환한 객체가 변수에 할당.
  3. 반환된 serColor를 호출하면 매개변수로 받은 값이 color 변수에 재할당.
  4. 반환된 init를 호출하면 color 변수에 red 값이 재할당.

🧐 위와 같이 클로저를 구현하면 참조하고 있는 렉시컬 환경이 살아있기 때문에 값을 참조하고 변경할 수 있다. 또한 반환된 함수들을 통해서만 변수에 접근할 수 있기 때문에 해당 변수는 은닉화할 수 있다.


🍑 오늘의 공부 일기

딥다이브를 2번째로 읽으면서 글로 정리가 될 만큼 1번째보다 이해가 수월하다고 느낀다. 1번째는 해당 개념들이 있다는 것에 더 초점이 맞혀졌는데 2번째 읽으면서 이 개념들이 왜 필요한지에 대해 더 생각해 본 거 같다. 앞으로도 딥다이브를 2-3번...여러번 읽으면서 자바스크립트에 대해 자세히 알아가보자.


참고 자료
이웅모, 모던 자바스크립트 Deep Dive, 위키북스,

profile
웹 프론트엔드 개발자

0개의 댓글