178일차 - Scope & Closure

김민찬·2021년 11월 4일
0

취업으로의 여정

목록 보기
183/196
post-thumbnail

JavaScript를 공부하기 시작한 뒤 19일차에 스코프와 클로저에 대해 언급한 적이 있다.
이를 기억하지 못하고 있다가 블로그를 되돌아 보던 중 발견했는데, 추후에 정리하겠다고 언급만 해놓고 정리를 하지 않았다. 오늘에서야 정리를 해본다.

스코프(Scope)

스코프는 이름처럼 범위에 관련된 내용이다.
그럼 무엇에 대한 범위일까?

모든 식별자(변수 이름, 함수 이름, 클래스 이름 등)는 자신이 선언된 위치에 의해 다른 코드가 식별자 자신을 참조할 수 있는 유효범위가 결정된다. 이를 스코프라고 한다. 즉, 스코프는 식별자가 유효한 범위를 말한다.

위의 설명처럼 참조할 수 있는 유효범위이다.

크게 나누면, 전역스코프 / 지역스코프 두 가지로 나눌 수 있다.

전역스코프

전역 스코프는 블록 밖에 선언된 변수를 예로 들 수 있다. 이러한 변수를 전역변수라고 부른다.
블록 밖에 선언되었기 때문에, 블록 내부를 포함한 어디서든 접근 가능하다.

const globalVariable = '전역변수';

function global () {
  return globalVariable
}

global() // '전역변수';

전역변수의 사용은 변수의 이름 중복과, 의도치 않은 재할당에 의한 상태 변화로 코드를 예측하기 어렵게 만 들 수 있다.

지역스코프

지역 스코프는 함수스코프와 블록스코프로 나눌 수 있다.

함수스코프

  • 자바스크립트는 기본적으로 함수스코프를 따른다. 이는 새로운 함수가 생성될때마다 새로운 스코프가 발생한다는 것이다.
  • 함수 안에서 선언된 변수는 해당 함수 안에서만 접근할 수 있다.
function sayHi () {
  const hi = 'hi';
  return hi;
}

sayHi(); // 'hi'
console.log(hi) // error, hi가 정의되지 않음

블록스코프

  • 블록스코프는 블록({})이 생성될 때 마다 새로운 스코프가 형성되는 것이다.
  • 원래 자바스크립트는 함수 스코프를 따르지만, let과 const 키워드의 등장으로 블록스코프 형성이 가능해 졌다.
{
  const hi = 'hi'
  console.log(hi); // 'hi'
}
console.log(hi); // error, hi가 정의되지 않음

클로저(Closure)

클로저 정의를 우선 보자

함수와 함수가 선언된 어휘적(lexical) 환경의 조합을 말한다. 이 환경은 클로저가 생성된 시점의 유효범위 내에 있는 모든 지역 변수로 구성된다.

여기서 어휘적 환경이란 말이 무엇인가 궁금해서 MDN을 찾아봤고 예제는 아래와 같다.

function init() {
  var name = "Mozilla"; // name은 init에 의해 생성된 지역 변수이다.
  function displayName() { // displayName() 은 내부 함수이며, 클로저다.
    alert(name); // 부모 함수에서 선언된 변수를 사용한다.
  }
  displayName();
}
init();

displayName() 내부엔 지역 변수가 없지만, 함수 내부에서 외부 함수의 변수에 접근할 수 있기 때문에 displayName() 역시 부모 함수 init() 에서 선언된 변수 name에 접근할 수 있다.
만약 displayName() 함수 내부에 변수 name이 있었다면 name대신 this.name을 사용 했을 것이다.
위 코드를 실행하면 displayName() 함수 내의 alert()문이 부모 함수에서 정의한 변수 name의 값을 성공적으로 출력한다.
이는 어휘적 범위 지정(lexical scoping)의 한 예이다. 여기서 "lexical"이란, 어휘적 범위 지정(lexical scoping) 과정에서 변수가 어디에서 사용 가능한지 알기 위해 그 변수가 소스코드 내 어디에서 선언되었는지 고려한다는 것을 의미한다. 단어 "lexical"은 이런 사실을 나타낸다. 중첩된 함수는 외부 범위(scope)에서 선언한 변수에도 접근할 수 있다.

처음에는 이해하기 어려웠으나 지속적으로 읽어보다 보니 이해가 갔다.
함수가 호출되는 동안 내부 lexical 환경에서 변수를 찾을때, 내부 lexical 환경에 없으면, 외부 그래도 없으면 전역 lexcial환경을 찾는 것을 어휘적 환경이라고 하는 것으로 이해했다.

그래서 어휘적 환경과의 조합이 무엇이고 클로저는 왜 특별한 것일까?

일반적인 함수는, 함수 실행이 끝나고 나면 함수 내부의 변수를 사용할 수 없다. 하지만 클로저는 외부 함수의 실행이 끝나더라도, 외부 함수 내 변수가 메모리 상에 저장된다.

위와 같은 특징 때문에 어휘적 환경의 조합이라고 말하는 것이다.
아래 예제들을 살펴보자.

더하기

const adder = x => y => x + y;

const add3 = adder(3);
add3(5); // 8
add3(10); // 13

클로저를 이용한 태그만들기

const tagMaker = tag => innerText => `<${tag}>${innerText}</${tag}>`;

const divMaker = tagMaker('div');
divMaker('hello'); // '<div>hello</div>'
divMkaer('world'); // '<div>world</div>'

외부함수의 실행이 끝났어도 외부 함 수 내 변수가 저장되어 있음을 볼 수 있다.
그리고 클로저의 또 하나의 특징은 은닉화이다.
함수가 선언될 때의 어휘적 환경을 기억하고 있어, 외부에서 접근해서 바꿀 수 없다.

참고자료

모던 자바스크립트 Deep Dive - 이웅모 지음
PoiemaWeb - 스코프
Howdy - 스코프
PoiemaWeb - 클로저
MDN - 클로저
코딩아마 - 클로저
코드스테이츠 커리큘럼 중 - 클로저 예제활용

profile
두려움 없이

0개의 댓글